前言
继续了解构造函数
正文
列表初始化
构造函数最直接的就是初始化成员变量
1 2 3 4 5 6 7 8 9 10 11
| #pragma once class Hack{ public: int hp; int mp;
Hack(int hp,int mp){ this->hp = hp; this->mp = mp; } };
|
除了在函数里面初始化,还可以使用列表方式
1 2 3 4 5 6 7 8 9 10
| #pragma once class Hack{ public: int hp; int mp;
Hack(int _hp, int _mp) :hp{ _hp },mp{_mp}{ } };
|
列表初始化的话就不能用this指针了,所以你要手动去区分开形参和成员变量,就是不要起一个名字
那么说这两种都是初始化的方法,谁更好
答案是列表更好
- 效率高
- 在某些情况下只能用列表初始化
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| #pragma once class Hack{ public: int hp=100; int mp=200;
Hack(int _hp, int _mp){ std::cout << hp << " " << mp << std::endl; this->hp = _hp; this->mp = _mp; std::cout << hp << " " << mp << std::endl; } };
|
当hp和mp有初始值,我们去看看执行的效果
可以看到先打印初始值在打印修改后的值
说明构造函数需要跳转需要空间。
再看看列表的情况。
1 2 3 4 5 6 7 8 9 10 11 12
| #pragma once #include<iostream> class Hack{ public: int hp=100; int mp=200;
Hack(int _hp, int _mp):hp{ _hp },mp{_mp}{ std::cout << hp << " " << mp << std::endl; std::cout << hp << " " << mp << std::endl; } };
|
看到都是实例化对象时候的值。
说明列表的方法实现是在执行函数之前完成的。
说明列表初始化的效率比传统在构造函数里初始化要快
至于某些情况要到继承的时候才知道
有优势就有劣势,在劣势方面,因为成员变量是顺序初始化,他没有等函数入栈前去看默认的值,但是如果调用的是未初始化的然后用来赋值就会造成问题
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| #pragma once #include<iostream> class Hack{ public: int h; int hp=100; int mp=200;
Hack(int _hp, int _mp) :hp{ _hp }, mp{ _mp }, h{ mp * 3 }{ std::cout << hp << " " << mp << " " << h << std::endl; std::cout << hp << " " << mp << " " << h << std::endl; } };
|
会发现h肯定不会这么大。说明传递的时候mp的值不正常。
原理就是,传参是从右往左传递,h声明的比mp早,mp还没完成初始化是个随机值,如果h声明在mp之下,那值就正常了。
只不过这种方式只是治标不治本,因为成员变量一多,没人回去顾及这些顺序问题,
所以列表初始化的顺序是从右往左的,传统构造函数内初始化是因为调用函数之前,成员变量都已经完成了各自的初始化
委托构造
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| #pragma once #include<iostream> class Hack{ public: int hp=100; int mp=200; int h;
Hack(int _h, int _hp):Hack(_h) { std::cout << h << " " << hp << std::endl; std::cout << h << " " << hp << std::endl; }
Hack(int _h) :h{ _h }{ std::cout << "hack _h" << std::endl; } };
|
会发现他先执行了_h
的构造函数。
委托构造函数只能用一个,且使用了委托构造之后不能再用列表初始化的方式了
副本构造函数
之前也用到过
其实就是一种拷贝的操作,编译器也同样为类指定了一个默认的副本构造函数,当然也可以手动指定。
看到这种右值引用的类型是我们没有写过的,这就是编译器自动产生的副本构造。
手写起来也不难
1 2 3 4 5
| Hack(Hack &hack){ this->hp = hack.hp; this->mp = hack.mp; this->h = hack.h; }
|
如果秉持不想改变这个传递的hack对象,也可以加上const
。取决于个人严谨程度,毕竟只是为了拷贝,万一被修改了也怪麻烦的。
然后就是利用列表的形式:
1 2 3
| Hack(Hack &hack) :hp{ hack.hp }, mp{ hack.mp }, h{ hack.h }{
}
|
活学活用。
虽然=也是会默认调用副本构造,但是第二次在使用=意义不同,因为第一次才是真正的构造这个类,第二次只是修改或者替换。
结语
同类型的类,在构造函数里是可以访问私有成员的。
构造函数目的就是初始化构造一个类。