简单分析一下C++中类的构造函数和初始化。
一、构造函数
(一)若类中没有显式定义任何构造函数的话,编译器会自动生成两个构造函数和一个重载操作符:
自动生成的构造函数有:
1、默认构造函数,即无参数的构造函数。
2、复制构造函数,即以该类的常引用对象对象为参数的构造函数。
自动生成的重载操作符为:
1、“=”操作符
(二)一旦类中定义了任何构造函数,编译器就不会自动生成默认构造函数。但复制构造函数和“=”重载操作符还是会自动生成。
(三)需要注意的是,若类中有const变量,则无法自动生成默认构造函数(const变量无法初始化),也无法自动生成“=”重载操作符(无法对const变量赋值)。
二、对象的初始化
譬如定义了类MyClass
class MyClass
{
public:
MyClass(int n){}
};
(一)直接初始化:
MyClass c1(1);
(二)复制初始化:
MyClass c2 = 1;
这里实际上调用了两次构造函数。第一次用1初始化了MyClass类的一个临时对象,第二次用这个临时对象通过复制构造函数初始化了MyClass对象c2。
情况许可时,可允许编译器把临时对象优化掉,跳过复制构造函数,直接创建对象。但这并不是编译器的义务。
*需要说明的是:
1、当编译器进行了优化时,其行为与直接初始化一致,而不必与复制初始化一致。譬如在VS2010Debug模式下:
class MyClass
{
public:
MyClass(int n){
d = n;
}
MyClass(const MyClass &c){
d = c.d + 1;
}
int d;
};
int main() {
MyClass c1(1);
MyClass c2 = 1;
cout << c1.d << endl;
cout << c2.d << endl;
return 0;
}
其两次输出均为1。
2、如果在构造函数前声明了explicit,则只允许使用直接初始化。复制初始化的形式会使编译器报错。
所以,上述程序只能改为:
class MyClass
{
public:
int d;
explicit MyClass(int n){
d = n;
}
MyClass(const MyClass &c){
d = c.d + 1;
}
};
int main() {
MyClass c1(1);
MyClass c2 = MyClass(1);
cout << c1.d << endl;
cout << c2.d << endl;
return 0;
}
有趣的是,此时若将复制构造函数的访问权限设为private,则主函数中第二行语句(c2的定义)会报错,表示无法访问复制构造函数。然而若将复制构造函数的访问权限恢复为public,在调试时,其并没有执行复制构造函数,而是同c1一样,使用直接初始化赋值。
即便将代码改成这样:
class MyClass
{
public:
int d;
explicit MyClass(int n){
d = n;
}
MyClass(const MyClass &c){
d = c.d + 1;
}
};
int main() {
MyClass c1(1);
MyClass c2(MyClass(1));
cout << c1.d << endl;
cout << c2.d << endl;
return 0;
}
c2初始化时,仍然只用了直接初始化的方式,调用了一次explicit MyClass(int n)构造函数后,便初始化完毕。
【注:以上两次代码修改在调试时均进行了监视,发现&c2与调用构造函数中的this的值相同】