From: http://blog.csdn.net/sailor_8318/archive/2008/11/19/3337236.aspx
struct CLS
{
int m_i;
CLS( int i ) : m_i(i) {}
CLS( )
{
CLS(0);
}
};
void main( void )
{
CLS obj1; // 调用无参数构造函数,再调用CLS(0)????;
CLS obj2(1);
cout << obj1.m_i << endl;
//-858993460
cout << obj2.m_i << endl;
//1
}
CLS(0)去掉或者改为CLS(2),结果都是一样的,证明上述结果只是一个随机数而已。为什么调用了CLS(0)没有任何作用呢??
需要清楚的是对象的创建是分两步来完成的
1、首先是先分配内存,如果是动态分配的话,需要调用operator new操作符,如果是作为一个局部变量由编译器在栈上分配内存
2、分配完内存以后,编译器或者new自动调用构造函数进行初始化
这两步是有先后顺序的,必须第一步完成以后才会做第二步的,假如在动态分配的时候内存分配失败,那编译器能够保证第二步不会执行。
显然上面代码中,CLS obj1;这里已经为obj1分配了内存,然后调用默认构造函数,但是默认构造函数还未执行完,却调用了另一个构造函数,这样相当于产生了一个匿名的临时CLS对象,它调用CLS(int)构造函数,将这个匿名临时对象自己的数据成员m_i初始化为0;但是obj的数据成员并没有得到初始化。于是obj的m_i是未初始化的,因此其值也是不确定的。会产生一个新的临时对象,可以观察一个在两个构造函数里面的this指针就知道了,构造函数执行完毕时随即析构该临时对象。
要想达到期望的效果,在构造函数里调用另一个构造函数的关键是让第二个构造函数在第一次分配好的内存上执行,而不是分配新的内存,这个可以用标准库的placement new做到
inline void *__cdecl operator new(size_t, void *_P)
{
}
调用上述的函数没有分配新的内存,这正是我们想要的。因此我们可以这样在一个构造函数里调用另一个构造函数:
class CTest
{
public:
int a;
int b;
CTest(int a, int b)
{
this->a = a;
this->b = b;
}
CTest(int a)
{
new(this) CTest(a,5);
}
};
int main(int argc, char* argv[])
{
CTest obj(1);
cout<< "a is " << obj.a << endl;
cout<< "b is "<< obj.b << endl;
return 0;
}
在C++中,成员函数的调用约定是thiscall,这个约定的意思是调用的时候第一个参数是个this指针。但是构造函数不会自动接收this指针,因为这个时候对象还没有成功建立呢。其实,构造函数和其他的成员函数也没多大的区别,最主要的区别是他一般是由编译器生成代码自动实现调用的,因此他不需要返回值(也没必要)。 但也能像调用普通成员函数一样调用构造函数,这个时候需要明确使用this来引用构造函数,如this->CTest::CTest()
class CTest
{
public:
int a;
int b;
CTest(int a, int b)
{
this->a = a;
this->b = b;
}
CTest(int a)
{
//new(this) CTest(a,5);
this->CTest::CTest(a,5);
}
};
这种情况下,第二个有参构造函数就是在第一个构造函数的基础上进行初始化。
若构造函数调用自身,则会出现无限递归调用,将会发生堆栈溢出。First-chance exception in ThinkingInCPlusPlus.exe: 0xC00000FD: Stack Overflow.
从这里,我们归纳如下:
1)在c++里,由于构造函数允许有默认参数,使得这种构造函数调用构造函数来重用代码的需求大为减少。因此应尽量避免这样使用,适用不当会带来意想不到的问题
2)如果仅仅为了一个构造函数重用另一个构造函数的代码,那么完全可以把构造函数中的公共部分抽取出来定义一个成员函数(推荐为private),然后在每个需要这个代码的构造函数中调用该函数即可
3)偶尔我们还是希望在类的构造函数里调用另一个构造函数,关键是让第二个构造函数在第一次分配好的内存上执行,而不是分配新的内存,这个可以用标准库的placement new或者明确通过第一个对象的this指针来调用第二个构造函数,此时相当于普通的函数调用。