首页 > 代码库 > 构造函数语意学 笔记(四)

构造函数语意学 笔记(四)

今天这篇是第二章的最后一篇笔记了。今天记录下成员们的初始化队伍这个章节。

若存在错误 请指正 万分感谢

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


构造函数语意学 笔记(四)