C++对象模型读书笔记(二)- 构造函数

误区

C++ 新手一般都有两个常见的误区:

  1. 任何 class 没有定义 default constructor ,那么就会被隐式的构造出来;
  2. 编译器合成的 default constructor 会显示的指定每个 data member 的值。

但是事实并非如此!

4种会让编译器必须为没有申明 constructor 的 class 合成一个 default constructor

  1. 当成员有 default constructor 的时候;
  2. 当基类有 default constructor 的时候;
  3. 当有虚函数的时候;
  4. 当有虚继承的时候。

以上构造出来的 default constructor 都是 non-trivial constructor ,如果没有满足上述情况且没有自定义 constructor 的时候,那么称编译器合成的是 trivial constructor (实际上不会被合成)。

什么是 non-trivial constructor ?什么又是 trivial constructor ?

字面意思进行理解:trivial 表示无用的,那么 non-trivial 就表示的是有用的。如果合成的构造函数会调用其他的 default constructor 的话,那么这个合成的 constructor 就是 non-trivial 的,反之,则是 trivial 的。

解释上述 4 种情况为何会这样

  1. 以代码举例
class Foo { public: Foo(), Foo(int) ... }
class Bar { public: Foo foo; char* str; }

void foo_bar() {
    Bar bar;
    
    if (str) { } ...
}
复制代码

请注意!编译器合成的 default constructor 并不会初始化 str ,这是需要程序员自己初始化的!编译器合成版本只负责初始化可以调用 default constructor 版本的部分。

于是 Bar() 应该这么写:

Bar Bar() {
    str = 0;
}
复制代码

编译器看到 Bar bar 语句会发生如下操作:

Bar() {
    foo.Foo()::foo(); // 附加的 compiler code
    str = 0;
}
复制代码
  1. 与 1 情况类似,需要注意的是,如果设计者提供了多个 constructor 版本,没有 default constructor 呢?编译器不会合成新的 default constructor 版本,而是扩张现有的每一个 constructors ,将“用以调用所有必要的 default constructors”的程序代码插入进去。

  2. 对于拥有 virtual function 的 class ,那么必须为没有申明 constructor 的 class 构造一个 default constructor ,原因有俩:

    • 每个类都会在编译时期产生出一个 virtual function table ,用于存放 class 的 virtual function 的地址
    • 每一个 class object 中,一个额外的 vptr 会被编译器合成出来。

    为了使虚函数的机制(多态)发挥出来,在 constructor 时期,编译器会为每一个 class object 安插合适的 virtual table 和为 vptr 设置初值。

  3. 同样的,对于虚继承的 class 而言,在 class object 构造期间,也会在 constructor 中安插“允许每一个 virtual base class 的执行期存取操作”的代码。

© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享