1.Default constructor的建构操作
首先强调两个误区:
(1)任何class如果没有定义default constructor,就会被合成出一个来
实际上,并不是所有的情况都会合成default constructor函数,当满足四种情况时,才会合成默认构造函数
而没有定义default constructor并且不符合以下将要提到的四种情况时,我们说它拥有的是隐含的无用默认构造函数,实际上它不会被合成出来
(2)编译器合成出来的默认构造函数会明确设定class内每一个数据成员的值
编译器不负责对数据成员的赋值工作,并不是编译器所需的,应该由程序员来完成工作(除非其有默认构造函数的subobject或member进行初始化)
下面来列出会合成默认构造函数的四种情况:
·类成员存在默认构造函数,在合成默认构造函数时必须调用该成员的默认构造函数或扩充到已有的默认构造函数中
·所继承的基类存在默认构造函数,在合成的默认构造函数中调用该基类的默认构造函数
·含有虚函数的类,编译器会合成默认构造函数,主要是初始化虚表指针
·虚继承的类,编译器会合成默认构造函数,同样要初始化虚表指针,指向虚基类
当已经显示定义了构造函数,即使不是默认构造函数,编译器也不会合成默认构造函数。如果显示定义的构造函数没有完成一些默认构造的功能,编译器会扩充显示定义的构造函数,调用能够调用的默认构造函数比如成员默认构造函数或基类的默认构造函数
2. Copy constructor 的建构操作
有三种情况会使用复制构造函数
·以同类型的对象初始化另一个对象
·函数参数以值传递的形式传递时
·以一个对象作为返回值时
复制构造含有两种方式:memberwise和bitwise(位逐次拷贝)
memberwise就是把每一个内建或者派生的数据成员的值,从一个对象复制到另一个对象。不过类成员则需要递归地进行memberwise方式构造
当没有显示定义的复制构造函数时,如果复制构造方式为memberwise就会合成默认复制构造函数,如果是bitwise就不会合成默认的复制构造函数
当满足以下四种情况时,不会展现bitwise复制构造特性:
·类成员含有显示复制构造函数(无论是显示定义还是编译器合成的)
·类的基类含有复制构造函数,编译器会合成默认的复制构造函数。这样保证当用派生类对象对基类对象初始化时,基类对象指向正确的虚函数表,而不是单纯地复制vptr,这样基类的vptr会指向继承类的虚函数表
·虚拟继承的类,编译器将会构造默认的复制构造函数,这样当基类对象被派生类对象初始化时,可以保证虚拟基类对象的指针被正确设置,因为虚拟基类对象在不同的子类中的位置是不同的
·类中含有虚函数时,表明一起会合成默认复制构造函数,当基类对象被派生类对象初始化时,可保证vptr指向正确的虚函数表
3.程序转化语意学
NRV编译器优化:前提是编译器提供该服务,且类中存在复制构造函数,无论是编译器合成的还是自定义的,否则优化无从谈起,因为trival复制构造函数就是最有效率的构造方法,有些类为了使用NRV优化甚至强行提供显示复制构造函数。
NRV优化主要用在返回值为对象的函数身上,主要方法是减少临时对象的数目来提高效率。
优化前:
X bar()//将会产生临时对象存储返回的值
{
X xx;
//处理
return xx;
}
优化后:
void bar(X &_result)//这样在指定函数返回对象是将不会产生临时对象
{
X xx;
//处理xx
//调用复制构造函数
_result.X::X(xx);
return;
}
X yy=bar();相当于bar(yy);
但是,当bar()单独使用的时候,仍然会产生一个临时对象存储结果,因为函数没有返回的对象。
而NRV优化又是在上述的基础上直接对_result上处理,不需要在函数内部产生一个xx对象,然后再调用复制构造函数,这样就减省了复制构造的消耗,但是会使用默认构造函数。
void bar(X &_result)//NRV优化
{
_result.X::X();
//对_result直接进行处理
return;
}
4. 成员初始化列表
以下四种情况必须使用成员初始化列表进行初始化:
·初始化一个引用成员
·初始化一个const成员
·当调用一个基类的构造函数或复制构造函数,而它拥有一组参数
·当调用一个类成员的构造函数时,它拥有一组参数
成员初始化顺序由成员声明顺序决定