一、初始化时带括号和不带括号的区别
class Test4 {
public:
Test3 t3;
Test4(){
t3 = Test3();
cout << "Test4" << endl;
}
};
int main()
{
Test3 t3;
cout << "忽略上面" << endl;
Test4 t4;
// Test4 t4(); // 不过是声明一个返回值为Test4的空参函数,区别大了
system("pause");
return 0;
}
千万不要搞混带括号和不带括号的情况。创建类和声明函数不一样。
二、类成员中包含其他类时的初始化
上面的例子中输出的结果是什么呢
Test3构造了两次,说明Test3在Test4声明时就已经调用了构造函数。
Test4分配内存时,会给Test3也分配内存(因为不是new出来的),所以会直接调用Test3的构造函数来创建。
如果不想这样的话,可以使用指针解决这个问题。变成Test3 *t3,这样不过是声明了一个指针而且没有new,只有在new的时候才会创建而调用构造函数。
三、初始化列表和构造函数的区别
Test3做了改动,看下面的例子
class Test3 {
public:
int a;
Test3() {
a = 0;
puts("Test3 constructor");
}
Test3(Test3 &t3) {
this->a = t3.a;
puts("Test3 copy constructor");
}
Test3& operator=(Test3 &t) {
puts("Test3 assign operator");
this->a = t.a;
return *this;
}
~Test3() { }
};
class Test4 {
public:
Test3 t3;
//Test4(){
// t3 = Test3();
// cout << "Test4" << endl;
//}
// 1. 创建临时变量,传参时隐藏的对临时变量(形参)进行初始化,调用拷贝构造
// 2. 走进构造函数中,因为t3不是指针而且没有分配内存,所以此时调用t3的默认构造函数来创建。
// 3. 用创建好的临时变量进行赋值,调用t3的赋值重载
//Test4(Test3 t3)
//{
// cout << "进入Test4构造函数里面了" << endl;
// this->t3 = t3;
//};
// 1. 不创建临时变量
// 2. 走进构造函数中,因为t3不是指针而且没有分配内存,所以此时要调用t3的默认构造函数来创建。
// 3. 用创建好的临时变量进行赋值,调用t3的赋值重载
//Test4(Test3& t3)
//{
// this->t3 = t3;
//};
// 1.传参时隐藏的对临时变量进行初始化,调用拷贝构造
// 2.在初始化列表中对成员变量进行初始化,调用拷贝构造
// 3. 因为成员变量已经初始化了,所以不需要再创建一次。
//Test4( Test3 t3) : t3(t3)
//{}
// 1.在初始化列表中对成员变量进行初始化,调用拷贝构造
//Test4( Test3 &t3) : t3(t3)
//{}
Test4(Test3 t3) : t3(t3)
{
this->t3 = t3;
};
};
int main()
{
Test3 t3;
cout << "忽略上面" << endl;
Test4 t4(t3);
// Test4 t4(); // 不过是声明一个返回值为Test4的空参函数,区别大了
system("pause");
return 0;
}
不同的构造函数依次的结果是:
最后一次的结果呢?
Test4(Test3 t3) : t3(t3)
{
this->t3 = t3;
};
答案是:
总结:
1. 函数参数在值传递时,其实是用的初始化的形式对临时变量进行赋值的,所以会调用拷贝构造函数。
2. 初始化和赋值的区别。
(1)对内置类型的成员(比如int,char等)没有什么的区别
(2)针对自定义的类。初始化调用的是拷贝构造函数,如果没有自己定义的话,是按照位来赋值的。所以成员变量中有指针可能会有问题,这种情况建议写自己的深拷贝构造函数。赋值时用=,如果未重载,走编译器自己默认的。
3. 初始化顺序是:
(1)先对形参(临时变量)赋值
(2)然后对初始化列表进行拷贝赋值(所以const和引用可以直接在这里赋值,下面会说)
(3)然后进入构造函数体中,查看所有的成员变量是否已经初始化,如果没有初始化的进行初始化,调用对应构造函数。
(4)之后就是显示的赋值等内容了,会调用重载的赋值运算符。
4. 初始化列表直接初始化,少了一次成员对象创建的过程,所以效率较高。
5. 补充一点,成员是按照他们在类中出现的顺序进行初始化的,而不是按照他们在初始化列表出现的顺序初始化的。
四、静态数据成员和const成员
详情参考:https://blog.csdn.net/theprinceofelf/article/details/20057359