在C++中,为什么构造函数不能有返回值?
在C++中,为什么规定构造函数不能有返回值?这其中有没有什么原因?
[解决办法]
假设有一个类C,有如下定义:
class C
{
public:
C():x_(0) {}
C(int i):x_(i) {}
private:
int x_;
};
如果C的构造函数可以有返回值,比如int:
int C():x_(0) { return 1; //1表示构造成功,0表示失败}
那么下列代码会发生什么事呢?
C c=C(); //此时c.x_==1!
很明显,C()调用了C的无参数构造函数。该构造函数返回int值1。恰好C有一个但参数构造函数C(int i)。于是,混乱来了。按照C++的规定,C c=C();是用默认构造函数创建一个临时对象,并用这个临时对象初始化c。此时,c.x_的值应该是0。但是,如果C::C()有返回值,并且返回了1(为了表示成功),则C++会用1去初始化c,即调用但参数构造函数C::C(int i)。得到的c.x_便会是1。于是,语义产生了歧义。使得C++原本已经非常复杂的语法,进一步混乱不堪。
构造函数的调用之所以不设返回值,是因为构造函数的特殊性决定的。从基本语义角度来讲,构造函数返回的应当是所构造的对象。否则,我们将无法使用临时对象:
void f(int a) {...} //(1)
void f(const C& a) {...} //(2)
f(C()); //(3),究竟调用谁?
对于(3),我们希望调用的是(2),但如果C::C()有int类型的返回值,那么究竟是调(1)好呢,还是调用(2)好呢。于是,我们的重载体系,乃至整个的语法体系都会崩溃。
这里的核心是表达式的类型。目前,表达式C()的类型是类C。但如果C::C()有返回类型R,那么表达式C()的类型应当是R,而不是C,于是便会引发上述的类型问题。
[解决办法]
不管在栈上定义对象的表达式,还是通过new动态创建对象的表达式。其值即为一个对象,或一个指向对象的指针,如果除了这个值之外,它还拥有另外一个“返回值”,那做何解?
试想,如果string的构造函数返回int的话,那下面这种语句会不会让人觉得太奇怪了点?
int i = string( "abcdefg ");
[解决办法]
呵呵,有时讨论一下这样的话题是有好处的。
longshanks(longshanks) 和 taodm((不能收CSDN社区短信息,请莫浪费精力)) 的回复都很好,学习一下。
我也觉得,构造函数和析构函数不需要返回类型,因为没有必要。
此外,它们是为面向对象而增加的新概念,确实无须符合一般函数的情形,以兼容C。而且,这个区别恰好突出了构造函数和析构函数的OO特性,我想C++的设计者当初可能就有这样的意图吧。虽然方案可以有很多种,但是如果加上这个意图,那么没有返回类型就顺理成章了。
再者,从书写角度看 classname classname(); 的写法多少有些冗余。既然它们已经够特殊了,再特殊一下也很自然吧。
这两点,对类型转换函数也是适用的。
至于表达式类型和函数返回类型的区别,我觉得 marrco2005(高手前传) 的回复说明了这一点: C()的表达式类型之所以和C的构造函数返回类型(后者没有类型)不一样,是因为这里的C()不是函数调用。我认为,如果是一个函数调用,那么表达式类型应该就是函数的类型返回类型。因为函数调用本身是一个后缀表达式,还是要符合C++标准中关于后缀表达式的说明的。所以,出现不一致的情况,标准中就会有另外的解释(不解释成一个函数调用),以达到自恰的目的。同样的,当我们用 new 或 delete 时,它们的返回类型也可以不同于构造函数/析构函数。因为前者是表达式,虽然可以把表达式看成近似于调用了一个函数,但实际上不是。
简单的说,函数调用也只是一种类型的表达式,不能从它的角度来看待表达式,而应该相反。C++标准也体现了这一点:表达式单独占据一个clause( 5[expr] ),而函数没有(特别地,标准将C++中的特殊函数,包括构造、析构、类型转换函数等等这些OO特性归结到一个clause中 12[special] )。关于函数调用的标准描述,可以参看 5.2.2 [expr.call] 。
[解决办法]
1. 标准
2. 其实是有返回值的,返回this
也就是说
class foo{
foo(){
return;
}
};
其实等价于
class foo{
foo(){
return this;
}
};
3. 返回void? 见2,当然不能写void在前面,因为其实是返回this
4. 手动调构造函数? 当然可以,你想怎么调用,就怎么调用,考虑下面的例子
class a{
public:
virtual void change();
virtual void foo(){
std::cout < < "i am a. " < < std::endl;
}
};
class b{
public:
virtual void change(){
new(this) a;
}
virtual void foo(){
std::cout < < "i am b. " < < std::endl;
}
};
void a::change(){
new(this) b;
}
int main()
{
a * p = new a;
p-> change();
p-> foo();
p-> change();
p-> foo();
delete p;
}
5. 在其他语言里面,有构造函数返回this
7. 返回,不返回, 在汇编上看,就是ret或jmp之前改不改变一个寄存器,或内存地址,改变之后调用方用不用.
8. 在写编译器的人来看,无非就是加一个特性,会不会导致语法冲突.
9. 从思考整个语言体系是否自洽,完整的人来看,应该返回this, 见2