C++-----深度探索对象模型-第二章-构造函数语义学(二)
1、在严谨的C++用词中,定义是指占用内存的行为。
2、显示初始化
X x0;
void foo()
{
X x1(x0);
X x2=x0;
X x3=X(x0);
}
如上的定义,每一个都明显以x0初始化其对象。那么必要的程序转换有两个阶段,1)重写每一个定义,其中的初始化操作会被剥除。2)类的拷贝构造调用操作被安插进去。这样上面的代码可能会变成下面这样:
void foo()
{
X x1;
X x2;
X x3;
x1.X::X(x0);
x2.X::X(x0);
x3.X::X(x0);
}
上面的x1.X::X(x0)就表现出对拷贝构造函数的调用。
3、参数初始化:把一个类对象作为参数传给一个函数参数或者作为一个函数的返回值,相当于以下形式初始化
//其中xx代表形式参数,或返回值,而arg代表真正的参数值。
X xx = arg;
foo(xx);
其实就是在过程中创建一个临时对象,并调用拷贝构造函数将其初始化,然后将此临时性的对象传递给函数,在函数使用完之后,自动调用destructor销毁临时的对象。
4、返回值的初始化其实也一样,首先在函数中加上一个而外的参数,参数类型是类对象的引用,这样函数返回值就是void,这个参数用来存放返回的值,在return 之前安插一个拷贝构造函数调用操作,将想要传回的值作为额外加的参数的初值。
5、编译器的优化:NRV(named return value),其实就是上面说到的这一点,在函数中加上一个参数,这个参数是类对象的引用,然后直接在调用拷贝构造函数即可。
6、当函数以传值形式返回一个类对象,当该类有一个拷贝构造函数时,编译器会对你的代码进行一定的优化。
7、下面的程序
X xx0(1024);
X xx1=X(1024);
X xx2=(X)2014;
含义都是一样的,都是以1024来初始化一个X的对象,但是在第二行第三行显然比第一行效率更差,原因是,它们两个在这个过程中首先创建一个临时对象,将这个临时对象初值设为1024,再以这个对象调用拷贝构造函数为定义的对象赋值,然后再调用析构函数将临时对象析构,而第一行只需要调用一次构造函数即可。
8、如果一个类,的默认拷贝函数被视为trivial,既没有任何成员是一个类或者一个基类,也没有任何的虚基类或虚函数,这个类可以不涉及拷贝构造函数。
9、如果出现以下四种情况:必须使用成员初始化列表:
1)当初始化一个引用成员时。
2)当初始化一个const member。
3)当调用一个基类的构造函数,而它有一组参数时。
4)当调用一个成员类对象的构造函数,而它有一组参数。
10、在类的设计中尽量使用成员初始化列表,如果不使用初始化列表,在函数体中进行初始化,会多出很多临时操作。
11、成员初始化列表的项目顺序是按照类中成员声明顺序决定的,不是由初始化列表的排列顺序决定的。
12、根据上一条,以下代码:
class X{
int i;
int j;
public:
X(int val):j(val),i(j){};
}
看上去像是没什么错误,先用val初始化j,再用j初始化i。但是其实并不是,因为i的定义在j之前,所以成员列表初始化中先初始化i,而此时j还没有初始化,是一个未定义的值。i(j)的结果是一个无法预知的值。这个问题很难被检查出来,很多编译器甚至不会提供警告。
13、成员初始化列表在任何显式定义操作之前。
14、可以使用构造函数体内一个成员而不要使用存在于成员初始化列表中的成员来为另一个成员设置初值。
15、编译器会对初始化列表异议处理可能重新排序,反映出成员声明顺序,会安插一些代码到构造函数体内,并置于任何显示用户代码之前。

浙公网安备 33010602011771号