1:当C++类导入继承机制,出现虚函数之后,vptr使得class产生了膨胀作用
- 构造函数附加了代码,将vptr初始化
- class必须合成一个拷贝构造函数和赋值构造函数,其现在是nontrival的(简单的位拷贝不能满足需求)。但其默认的析构函数仍然是trival的
2:C++标准要求编译器尽量延迟必要成员函数(构造、拷贝)的实际合成操作,直到真正需要时才合成。
3:拷贝构造函数的需要点:以传值的方式传回一个local class object ,就需要一个拷贝构造函数,即使是默认的位逐次拷贝已经足够,因为合成的拷贝构造函数的出现会触发NRV优化,而NRV优化后将不需要再调用拷贝构造函数,因为运算结果已经直接在“被传回的对象”体内直接计算了。
4:继承体系下的对象构造,当有一个构造函数(无论是显式定义或是编译器合成)被调用时,真正伴随着构造函数进行了一下扩充
- 成员初始化列表中的数据成员初始化操作会被放进构造函数的函数本体,并以成员声明的顺序进行初始化
- 如果有一个成员没有出现在成员初始化列表之中,但是该成员有一个默认构造函数,那么该默认构造函数必须被调用
- 如果class中有虚函数机制出现,那么对象中的vptr必须被构造函数设定初值,以指向正确的vbtl(先于前两条)
- 所有上一层的base class 的构造函数都必须被调用,以base class 的声明顺序进行调用(先于前两条)
- 如果base class 被列于成员初始化列表中,那么任何显式指定的参数都应该被传过去
- 如果base class 未被列于成员初始化列表,那么其默认构造函数会被调用(或者是逐成员拷贝构造函数)
- 如果base class 是多重继承下的第二或后继的基类,那么this指针必须有所调整
- 所有虚基类构造函数都必须被调用,从左到右,从深到浅
- 同上1
- class 中的每一个虚基类类对象的偏移位置offset必须在执行期可被存取
- 如果类对象是最底层(most_derived)的class ,其构造函数可能被调用:用以支持这一行为的机制必须被放进来
5:在声明一个拷贝构造函数和拷贝运算符时,一个新手及其容易陷入的错误就是在其中忘记检查自我指派(拷贝内容与自身相同)的操作是否正确。if(this == &rhs),面对自我拷贝时,需要进行优化判断。
6:如果将一个class的析构函数定义为inline,则每一个调用操作都会在调用地点扩展开来。
7:虽然一个类的析构函数是virtual,但是其调用操作(在包含类的析构函数中)会被静态的决议出来。
8:虚拟继承中,派生类的构造函数本体必须条件式的测试传进来的参数,然后决定调用或者不调用相关虚基类的构造函数。结果就是由最底层的派生类进行虚基类的构造函数的调用,这种机制会压制中间层级class中构造函数对虚基类构造函数的调用。
9:在一个class的构造函数中调用一个虚函数,其函数实例应该是在此class中有作用的那个。
10:虚拟机制本身必须知道是否调用源自于一个构造函数之中,因为在构造函数之中调用虚函数,可能需要以静态的方式进行决议。根本的决定方法是:在执行一个构造函数时,必须限制一组虚函数的候选名单。这组候选名单明显可以由vptr来进行控制。
11:设定vptr是编译器要做的事,程序员无需考虑。
12:vptr初始化的时机,在基类构造函数调用之后(这样每一次它都能够调用正确的虚函数实例),在成员初始化列表或是程序员的代码之前。
13:防止类对象的拷贝,可以讲拷贝运算符的声明为private,并不提供其定义即可
- friend和member function仍然可以调用
- 不提供其定义,一旦某个friend或是member function进行拷贝,会在链接期间失败,这会被认为是链接器 的问题。
14:并没有什么理由要阻止拷贝一个普通的class object,所以问题转化为“默认的操作是否足够”,拷贝赋值运算符必须出现在的四种情况(未展现出按位拷贝的语意,也就是说按位拷贝不能满足要求了)
- class中含有一个member object,而该对象有一个拷贝赋值运算符
- class的基类中有一个拷贝赋值运算符时
- class中声明了虚函数
- class继承自一个虚基类
15:当默认操作足够时,我们可能还是需要定义一个拷贝构造函数,以打开NRV优化,但是对于拷贝赋值运算符就不是这样了,只要按位拷贝足够就不需要再进行合成。