写一个类的时候,我们可能会希望有多重不同的初始化方式,此时就用到了重载构造函数。例如:
class TestConstructor
{
public:
int value;
public:
TestConstructor();
TestConstructor(int);
};
这样我们可以有两种方式来初始化一个TestConstructor对象。
两个重载构造函数实现的代码可以分别是:
TestConstructor::TestConstructor()
{
this->value = 10;
}
TestConstructor::TestConstructor(int value)
{
this->value = value;
}
对于上边的这个无参构造函数,我们可以直接初始化value为10,但是如果参数很多,重载构造函数很多,那么就不可能每个都写一遍一模一样的赋值操作(其实也可以,就是有些丑,而且如果发生改动,则有可能会因为疏忽而出错)。所以,我们希望能够减少重复的赋值操作,例如调用其他的重载构造函数,就像这样,
TestConstructor::TestConstructor()
{
TestConstructor(10);
}
TestConstructor::TestConstructor(int value)
{
this->value = value;
}
但是这样可以嘛?我们试一下,为了方便查看实际生成的对象,我们在构造函数里输出对象的指针地址。
#include
using namespace std;
class TestConstructor
{
public:
int value;
public:
TestConstructor();
TestConstructor(int);
};
TestConstructor::TestConstructor()
{
TestConstructor(10);
cout << "constructor1:" << this << endl;
}
TestConstructor::TestConstructor(int value)
{
this->value = value;
cout << "constructor2:" << this << endl;
}
int main()
{
TestConstructor *t = new TestConstructor();
cout << t->value << endl;
cout << t << endl;
delete t;
return 0;
}
运行结果:
constructor2:0x7fff81eb3494
constructor1:0x5586774bce70
0
0x5586774bce70
可以看出,在构造函数中调用重载构造函数时,被调用的构造函数重新创建了一个新的对象,所以不能用这种方法。
在我之前的项目中,为了解决这个问题,我采用了另外写一个private的函数的方法,把一些需要反复初始化的部分扔进了一个private的函数里。例如,
class TestConstructor
{
private:
void initialize(int);
public:
int value;
public:
TestConstructor();
TestConstructor(int);
};
void TestConstructor::initialize(int value)
{
this->value = value;
}
TestConstructor::TestConstructor()
{
this->initialize(10);
cout << "constructor1:" << this << endl;
}
TestConstructor::TestConstructor(int value)
{
this->initialize(value);
cout << "constructor2:" << this << endl;
}
这个当然是一种解决方案。不过,最近用cppcheck analysis的时候,总是提示我 Member variable "xxx" is not initialized in the constructor.
, 这促使我思考了一下是否有更好的方法解决该问题。
在C++的函数声明中可以设置默认参数,所以一种可行的方法就是用含默认参数的构造函数来代替重载构造函数。如:
class TestConstructor
{
public:
int value;
public:
TestConstructor(int value = 10);
};
TestConstructor::TestConstructor(int value)
{
this->value = value;
}
(NOTE:注意默认参数通常放在函数声明上,并且只能写一次,即声明写了,定义部分就不能再写)
C++提供在预先分配好的内存上创建对象的方法,即placement new运算符。在C++中,构造函数的执行实际上分为两个阶段,分别是初始化阶段和计算阶段,其中初始化阶段先于计算阶段。在进入计算阶段前,所有的成员变量都已经完成了初始化,所以内存也都已经按需分配好,此时我们可以在this指针指向的这块内存上调用重载构造函数创建一个新的对象,覆盖掉原来的对象。
#include
using namespace std;
class TestConstructor
{
public:
int value;
public:
TestConstructor();
TestConstructor(int value);
};
TestConstructor::TestConstructor()
{
new (this)TestConstructor(10);
cout << "constructor1:" << this << endl;
}
TestConstructor::TestConstructor(int value)
{
this->value = value;
cout << "constructor2:" << this << endl;
}
int main()
{
TestConstructor *t = new TestConstructor();
cout << t->value << endl;
cout << t << endl;
delete t;
return 0;
}
运行结果:
constructor2:0x55bece811e70
constructor1:0x55bece811e70
10
0x55bece811e70
据我了解这是C++11中加入的功能,名为委托构造函数。其将重载构造函数放置在初始化列表的位置调用。
#include
using namespace std;
class TestConstructor
{
public:
int value;
public:
TestConstructor();
TestConstructor(int);
};
TestConstructor::TestConstructor():TestConstructor(10)
{
cout <<"constructor1:"<< this << endl;
}
TestConstructor::TestConstructor(int value)
{
this->value = value;
cout <<"constructor2:"<<this << endl;
}
int main()
{
TestConstructor *t = new TestConstructor();
cout << t->value << endl;
cout << t << endl;
delete t;
return 0;
}
运行结果:
constructor2:0x5594d644de70
constructor1:0x5594d644de70
10
0x5594d644de70
##总结
对于重载构造函数的互相调用初始化成员变量的问题,我们可以