【深度探索C++对象模型读书笔记】【第2章】构造函数语意学

一、Default Construct的构造操作

1、 C++ Standard说:对于一个类,如果没有用户定义的构造函数,那么会有一个默认构造函数被隐式声明出来,但这个构造函数是trivial constructor(没啥用的)。

2、 以下4种情况编译器会合成nontrivial default construct:

a)  带有default constructor的member class object

如果一个class没有任何constructor,但它内含一个member object,而后者有default constructor,那么编译器需要为该class合成一个default constructor。

如果一个class内含一个或一个以上的memberclass objects,那么这个class的每一个constructor必须调用每一个member class的default constructor。编译器会扩张已存在的constructors,在其中安插一些代码,使得user code被执行之前先以“member objects在class中的声明顺序”来调用各个default constructors。

例子:

class Dopey{ public:Dopey(); ...};
class Sneezy{ public:Sneezy(int); Sneezy(); ... };
class Bashful{ public:Bashful(); ... };

class Snow_White{
public:
	Dopey dopey;
	Sneezy sneezy;
	Bashful bashful;
	// ...
private:
	int mumble;
};

Snow_White::Snow_White():sneezy(1024){
	mumble = 2048;
}
// 编译器扩张后的default constructor
Snow_White::Snow_White() : sneezy(1024)
{
	// 插入member class object
	// 调用其constructor
	dopey.Dopey::Dopey();
	sneezy.Sneezy::Sneezy(1024);
	bashful.Bashful::Bashful();
	// explicit user code
	mumble = 2048;
}

b)   带有default construct的base class

如果一个没有任何constructors的class派生自一个“带有default constructor”的base class,那么这个derived class的default constructor会被视为nontrivial,并因此需要被合成出来。

c)   带有一个virtual function的class

下面两个扩张操作会在编译期间发生:

1.一个virtualfunction table会被编译器产生出来,内放class的virtual functions地址;

2.在每一个classobject中,一个额外的pointer member(也就是vptr)会被编译器合成出来,内含相关的class vtbl的地址。

d)   带有一个virtual base class的class

3、 在合成的default constructor中,只有base class subobjects和member class objects会被初始化,所有其它的nonstaticdata member(如整数、整数指针、整数数组等等)都不会被初始化。

二、Copy Construct的构造操作

1、 有三种情况,会以一个class的内容作为另一个class object的初值:

a)  对一个object作显示的初始化操作,如:X x2= x1;

b)  一个object被当作参数交给某个函数时,如:foo(x2);

c)  当函数返回一个class object时,如return x2。

2、 如果class没有提供一个explicit copy constructor,当class object以相同class的另一个object作为初值时,其内部是以所谓的default memberwise initialization手法完成的,即把每一个内建的或派生的data member的值,从一个object拷贝到另一个object。不过它并不会拷贝其中的member class object,而是以递归的方式施行memberwise initialization。

3、 default constructors和copy constructors在必要的时候才由编译器产生出来。“必要”意指当class不展现bitwisecopy semantics时。没有default constructors和copy constructors时,编译器通过bitwise copy来产生类,就是将源类中的成员变量中的每一位都逐次复制到目标类中。这样导致如果类中的成员变量有一个指针,则只拷贝了指针值,两个指针指向同一块内存。

#include<iostream>
using namespace std;

class Word{
public:
	Word(const char* s){
		str = new char[strlen(s) + 1];
		strcpy(str, s);
		cnt = strlen(s) + 1;
	}
	~Word(){ delete[] str; }
public:
	char* str;
	int cnt;
};

int main(){
	Word noun("book");
	Word verb = noun;

	cout << noun.str << endl;
	cout << noun.cnt << endl;
	cout << static_cast<void*>(noun.str) << endl; //两个对象的str指针指向同一块内存
	cout << static_cast<void*>(verb.str) << endl;//两个对象的str指针指向同一块内存

	system("pause");
	return 0;
}

4、  以下4种情况,一个class不展现bitwisecopy semantics:

a)   class内含一个member object而后者的class声明或被编译器合成有一个copy constructor时;

b)   当class继承自一个base class而后者存在或被编译器合成有一个copy constructor时;

c)   当class声明了一个或多个virtual functions时;

d)   当class派生自一个继承串链,其中有一个或多个virtual base classes时。

5、 编译器对于每一个新产生的class object的vptr不能成功而正确地设好其初值,将导致可怕的后果。当编译器导入一个vptr到class之中时,该class就不再展现bitwise semantics

6、  当一个base class objec以其derived class的object内容做初始化操作时,其vptr复制操作也必须保证安全。

7、  每一个编译器对于虚拟继承的支持承诺,都代表必须让derived class object中的virtual base classsubobject的位置在执行期准备妥当。维护位置的完整性是编译器的责任。

三、程序转化语意学

1、  显示的初始化操作都有两个必要的程序转化阶段:

a)   重写每一个定义,剥除其中的初始化操作;

b)   安插classcopy constructor调用操作。

2、 把一个class object当作参数传给一个函数或是作为一个函数的返回值,相当于以下形式的初始化操作:X xx = arg; 其中xx代表形式参数或返回值,而arg代表真正的参数值。形参必须从原先的类的对象改变为类的引用,避免复制。

3、  函数定义如下:

X bar(){
	X xx;
	//处理xx……
	return xx;
}

bar()的返回值通过一个双阶转化从局部对象xx中拷贝出来:

a)  首先添加一个额外参数,类型是class object的一个reference,这个参数用来放置被拷贝构建而得的返回值。

b)  return指令之前安插一个copy constructor调用操作,以便将欲传回之object的内容当作上述新增参数的初值,同时重写函数使它不返回任何值。

bar()转换如下:

void bar(X& _result){
	X xx;
	//编译器所产生的default constructor调用操作
	xx.X:X();
	//......处理xx
	//编译器所产生的copy constructor调用操作
	_result.X::X(xx);
	return;
}

4、  Named Return Value(NRV)优化如今被视为是标准C++编译器的一个义不容辞的优化操作,特点是直接操作新添加的额外参数。只有copy constructor的出现才会激活C++编译器的NRV优化。NRV优化虽然极大地改善了效率,但还是饱受批评:一是优化由编译器默默完成,而是否完成以及其完成程度完全透明;二是一旦函数变得比较复杂,优化就变得比较难以施行;三是优化由可能使程序产生错误——有时并不是对称地调用constructor和destructor。

四、成员们的初始化队伍

1、 必须使用member initialization list

a)  初始化一个reference member时;

b)  初始化一个const member时;

c)  调用一个base classconstructor,而它拥有一组参数时;

d)  调用一个member classconstructor,而它拥有一组参数时。

2、 编译器会一一操作initialization list,以适当顺序在constructor内安插初始化操作,并置于任何explicit user code之前。

3、 initialization list的项目顺序是由class中的members声明顺序决定的,不是由initialization list中的排列顺序决定的。

#include<iostream>
using namespace std;

class X{
private:
	int i;
	int j;
public:
	X(int val) :j(val), i(j){}
	void printij(){
		cout << i << endl; //乱七八糟的一个数
		cout << j << endl; //5
	}
};

int main(){
	X x(5);
	x.printij();

	system("pause");
	return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值