首页 > 代码库 > 构造函数语意学 笔记(四)
构造函数语意学 笔记(四)
今天这篇是第二章的最后一篇笔记了。今天记录下成员们的初始化队伍这个章节。
若存在错误 请指正 万分感谢
1.成员们的初始化队伍(Members Initialization List,MIL):
当我们定义一个Cstor 时,我们就可以初始化我们的members。
两种方法:
1.经由MIL。
2.在Cstor 内部。
2.本章介绍如下几点内容:
1. 何时使用MIL有意义。
2. 初始化过程中的内部操作
3. 陷进介绍
3.四种情况推荐使用MIL(有的甚至是必须使用):
1.初始化一个引用成员;
2.初始化一个const member;
3.调用基类的构造函数,并且有参数;
4.调用member class object 的构造函数,并且有参数。
4.解释:
1和2的解释:
#include <iostream> using namespace std; class Base{ int x, y; int& rx; const int z; //由于是const 的,所以不能赋值的,不能当左值,出现在赋值号的左边。只能初始化了。 public: /*Base(int var) :x(var), y(x){ //“Base::rx”: 必须初始化引用类型的成员,这个地方还是比较强调初始化和赋值的,引用在为初始化之前可以进行赋值操作嘛? //rx = x; 必须使用MIL操作。 }*/ friend ostream& operator<<(ostream& os, const Base& p){ os <<" p.x = "<<p.x<< " " <<" p.y = "<<p.y<< endl; return os; } }; int main(){ Base b1(2); cout << b1 << endl; system("pause"); return 0; }上面的例子测试一下很容易就发现了。
看一个稍微复杂点的例子:
#include <string> #include <iostream> using namespace std; class Word{ private: string _name; int _cnt; public: Word(){ _name =""; _cnt = 0; } }; //内部伪码:↓ Word::Word(){ _name.string::string();//默认构造函数调用。 string _temp = string("");//初始化临时对象。 _name.string::operator=(_temp);//注意地方是赋值,已经不是初始化了。 _temp.string::~_string();//析构掉临时对象。 _cnt = 0; }其实这样做花销挺大的,程序也能正确的编译运行,但是不推荐。
推荐的方式:
Word::Word() :_name(""){ _cnt = 0; }伪码:↓
Word::Word(){ _name.string::string("");//对比下上面的。 _cnt = 0; }
陷进:
template<class T> foo<T>::foo(T t){ _t = t;//这样做好嘛? }其实看T的类型了。如果是内置的基本类型,那着实无区别,但是像上面的string一样,那么真不好。
MIL似乎引导程序员疯狂使用MIL啊,即使是内置的类型。
5.内部行为:
编译器会按照你声明的顺序,一一进行处理MIL,并且在Cstor内安插相应的初始化代码,并且安插的代码一定是在用户自定义的代码之前的。
陷进:
#include <iostream> using namespace std; class Base{ int x, y; public: /*Base(int var) :y(var), x(y){ //这个地方的执行顺序? cout << "Using the default Cstor " << endl; }*/ friend ostream& operator<<(ostream& os, const Base& p){ os <<" p.x = "<<p.x<< " " <<" p.y = "<<p.y<< endl; return os; } }; int main(){ Base b1(2); cout << b1 << endl;//b1的值会怎么样呢? system("pause"); return 0; }这个地方就涉及到了顺序的问题了,上面已经介绍了按照声明顺序处理和插入代码。所以会先处理x,但是你的x是用y进行处理的,这个时候的y是垃圾值,所以x也成了垃圾值。
稍微改动下:
#include <iostream> using namespace std; class Base{ int x, y; public: /*Base(int var) :y(var){ x=y; }*/ friend ostream& operator<<(ostream& os, const Base& p){ os <<" p.x = "<<p.x<< " " <<" p.y = "<<p.y<< endl; return os; } }; int main(){ Base b1(2); cout << b1 << endl; system("pause"); return 0; }这样就完美啦,因为安插的代码是在自定义代码之前的。所以y的值是先被初始化的。所以问题解决了。
上面的问题目前只有g++编译器可以察觉,在我的vs2013上是无法察觉的。
好了就到这个地方了。
6.总结一下:
MIL操作被编译器转化,按照声明的顺序进行处理,并且总是安插在explicit user_code 之前。
7.大总结:
这个章节是写的最少一章的,别的章节每次总结都要花费几个小时,而且完美主义泛滥,总想排的精美点,肯定要占据时间的。希望自己的知识转化率可以高一点。
总结下三个章节的东西吧。
一个章节是写默认构造函数。
第二是写拷贝构造函数,
第三个是写程序转化语意学。最后这个就不说了。
每个章节我都尽可能把自己的理解写出来,尽可能的避免复制书本的内容,毕竟不是自己的。
而且第一次读的时候,有些地方还是不通畅的,所以有可能读第二次,并且会修正这篇博文中记录不好的地方。
其实我写这个博文最主要的目的是巩固自己的知识。我每天都是先阅读,拿纸笔做提纲,写一份笔记,然后在写成博文。其实就是抄书,人太笨没办法。
明天就要回家了,希望还能每天抽空写博客。因为我下一个阶段的目标是把那本C++ Primer 看完,希望时间充裕。
End
构造函数语意学 笔记(四)