首页 > 代码库 > C++ 对象模型--- 默认构造函数
C++ 对象模型--- 默认构造函数
“default constructor ... 在需要的时候被编译器产生出来。“ ------ 《深度探索C++对象模型》,P39(华中科技大学出版)
-------------------------------------------------------------------------------------------------------------------------------------------------------
C++ 新手一般有两个常见的误解:
- 任何 class 如果没有被定义 default constructor,就会被合成出来一个;
- 编译器合成出来的 default constructor 会明确设定 “class 内 每一个 data member 的 默认值”.
如你所见,没有一个是真的.
--------------------------------------------------------------------------------------------------------------------------------------------------------
在 以下 4 种情况下,编译器会 合成 构造函数
- case 1 当一个class 中 “带有 default constructor”的 member class object;
- case 2 当一个class 派生自 ”带有 default constructor“的 base class;
- case 3 当一个class 声明(或 继承)一个 virtual function;
- case 4 当一个class 派生自一个 继承串链,其中有一个 或 多个 virtual base class.
以上 4 种情况,会导致 “编译器 必须 为 未声明 constructor 的 classes 合成 一个 default constructor”. C++ Standard [ISO-C++95](不知现在 C++ standard 对此处的 描述 是否 有所变化) 把 那些合成物 称为 implicit nontrivial default constructor. 被合成出来的 constructor 只能满足编译器(而非程序)的需求. 它之所以能完成任务,是借着“调用 member object 或 base class 的 default constructor” 或是 “为每一个object 初始化 其 virtual function 机制 或 virtual base class 机制” 而完成的. 对于不存在 这 4 种情况 而又没有声明 任何 constructor 的 classes,我们说 它们拥有的是 implicit trivial default constructor,它们实际上并不会被合成出来.
在合成的 default constructor 中,只有 base class subobjects 和 member class objects 会被初始化,所有其他的 nonstatic data member,如整数,指针,整型数组等等都不会被初始化. 如果程序需要一个“把某指针设为 0 ”的 default constructor,那么提供它的应该是程序员.
测试环境: CentOS 6.5, g++ 4.4.7
下面通过 查看 反汇编代码(使用 objdump -d xxx.o 查看) 来确定 是否 有 default constructor 被合成
===================================================================================================================
1. 不是 上述 四种情况,也无用户定义的 constructor
1 #include <iostream> 2 3 class TEST_A // 不是 上述 四种情况,也无用户定义的 constructor 4 { 5 public: 6 int a; 7 }; 8 9 int main() 10 { 11 TEST_A test_a; 12 return 0; 13 }
反汇编: 可以看到 下面 汇编代码 中 并没有 call,即没有 函数调用,也就表明 在 定义 一个 TEST_A 对象时,没有 调用 constructor,说明 编译器 并没有为它合成 default constructor.
1 00000000 <main>: 2 0: 55 push %ebp 3 1: 89 e5 mov %esp,%ebp 4 3: 83 ec 10 sub $0x10,%esp 5 6: b8 00 00 00 00 mov $0x0,%eax 6 b: c9 leave 7 c: c3 ret
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
1 #include <iostream> 2 3 class TEST_A // 不是 上述 四种情况,也无用户定义的 constructor 4 { 5 public: 6 int a; 7 }; 8 class TEST_B:public TEST_A // 不是 上述 四种情况,也无用户定义的 constructor,TEST_B 继承自 TEST_A 9 { 10 public: 11 int a; 12 }; 13 14 int main() 15 { 16 TEST_B test_b; 17 return 0; 18 }
反汇编:TEST_B 虽然继承于 TEST_A(不是 虚拟继承),但是 TEST_A 并没有 default constructor,也不符合 上述 4 种情况,TEST_B 自身又无 member
object,也没有 virtual function,故 编译器 不会为它 合成 default constructor. 下面的 汇编代码中没有 函数调用,也证明了这点.
1 00000000 <main>: 2 0: 55 push %ebp 3 1: 89 e5 mov %esp,%ebp 4 3: 83 ec 10 sub $0x10,%esp 5 6: b8 00 00 00 00 mov $0x0,%eax 6 b: c9 leave 7 c: c3 ret
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
2. 不是上述 4种情况,有用户自定义 default constructor,或许 还有 自定义 destructor
1 #include <iostream> 2 3 class TEST_A // 不是 上述 四种情况,有用户定义的 default constructor 4 { 5 public: 6 TEST_A(){} 7 int a; 8 }; 9 10 int main() 11 { 12 TEST_A test_a; 13 return 0; 14 }
反汇编:有用户自定义的 default constructor,定义一个类对象时,自然 会 调用 default constructor. 没有调用 destructor:用户未定义,也不符合 编译器 合成
destructor 的 条件,故 没有 destructor 供其调用(也没有必要)
1 00000000 <main>: 2 0: 55 push %ebp 3 1: 89 e5 mov %esp,%ebp 4 3: 83 e4 f0 and $0xfffffff0,%esp 5 6: 83 ec 20 sub $0x20,%esp 6 9: 8d 44 24 1c lea 0x1c(%esp),%eax 7 d: 89 04 24 mov %eax,(%esp) 8 10: e8 fc ff ff ff call 11 <main+0x11> //调用 用户自定义 default constructor 9 15: b8 00 00 00 00 mov $0x0,%eax 10 1a: c9 leave 11 1b: c3 ret
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
1 #include <iostream> 2 3 class TEST_A // 不是 上述 四种情况,有用户定义的 constructor,destructor 4 { 5 public: 6 TEST_A(){} 7 ~TEST_A(){} 8 int a; 9 }; 10 11 int main() 12 { 13 TEST_A test_a; 14 return 0; 15 }
反汇编:由于 TEST_A 还定义了 destructor, 故 main 函数 结束时,还 对 TEST_A 的对象 进行了 析构
1 00000000 <main>: 2 0: 55 push %ebp 3 1: 89 e5 mov %esp,%ebp 4 3: 83 e4 f0 and $0xfffffff0,%esp 5 6: 53 push %ebx 6 7: 83 ec 2c sub $0x2c,%esp 7 a: 8d 44 24 1c lea 0x1c(%esp),%eax 8 e: 89 04 24 mov %eax,(%esp) 9 11: e8 fc ff ff ff call 12 <main+0x12> // 调用 用户自定义 default constructor 10 16: bb 00 00 00 00 mov $0x0,%ebx 11 1b: 8d 44 24 1c lea 0x1c(%esp),%eax 12 1f: 89 04 24 mov %eax,(%esp) 13 22: e8 fc ff ff ff call 23 <main+0x23> // 调用 用户自定义 destructor 14 27: 89 d8 mov %ebx,%eax 15 29: 83 c4 2c add $0x2c,%esp 16 2c: 5b pop %ebx 17 2d: 89 ec mov %ebp,%esp 18 2f: 5d pop %ebp 19 30: c3 ret
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
1 #include <iostream> 2 3 class TEST_A // 用户 自定义 default constructor, 未 定义 destructor 4 { 5 public: 6 TEST_A(){} 7 //~TEST_A(){} 8 int a; 9 }; 10 class TEST_B:public TEST_A // 普通 继承 11 { 12 public: 13 int a; 14 }; 15 16 int main() 17 { 18 TEST_B test_b; 19 return 0; 20 }
反汇编:属于 case 2,TEST_B 继承自带有 default constructor 的 base class:TEST_A. 故 编译器 会 为其 合成 一个 default constructor,以保证 可以调用
TEST_A 的 default constructor 生成 derived class:TEST_B 中的 TEST_A subobject. 此例 同样 没有 destructor 被 合成.
1 00000000 <main>: 2 0: 55 push %ebp 3 1: 89 e5 mov %esp,%ebp 4 3: 83 e4 f0 and $0xfffffff0,%esp 5 6: 83 ec 20 sub $0x20,%esp 6 9: 8d 44 24 18 lea 0x18(%esp),%eax 7 d: 89 04 24 mov %eax,(%esp) 8 10: e8 fc ff ff ff call 11 <main+0x11> // 调用 合成 的 default constructor 9 15: b8 00 00 00 00 mov $0x0,%eax 10 1a: c9 leave 11 1b: c3 ret
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
1 #include <iostream> 2 3 class TEST_A 4 public: 5 TEST_A(){} 6 ~TEST_A(){} 7 int a; 8 }; 9 class TEST_B:public TEST_A 10 { 11 public: 12 int a; 13 }; 14 15 int main() 16 { 17 TEST_B test_b; 18 return 0; 19 }
反汇编:由于 base class 中 定义了 destructor,故 要 析构 derived class 中的 base class subobject,就需要调用base class 的 destructor,编译器通过 为 derived class 合成 一个 destructor,并在该 合成的 destructor 中 调用 base class 的 destructor 来达到目的.
1 00000000 <main>: 2 0: 55 push %ebp 3 1: 89 e5 mov %esp,%ebp 4 3: 83 e4 f0 and $0xfffffff0,%esp 5 6: 53 push %ebx 6 7: 83 ec 2c sub $0x2c,%esp 7 a: 8d 44 24 18 lea 0x18(%esp),%eax 8 e: 89 04 24 mov %eax,(%esp) 9 11: e8 fc ff ff ff call 12 <main+0x12> // 调用 合成 的 default constructor 10 16: bb 00 00 00 00 mov $0x0,%ebx 11 1b: 8d 44 24 18 lea 0x18(%esp),%eax 12 1f: 89 04 24 mov %eax,(%esp) 13 22: e8 fc ff ff ff call 23 <main+0x23> // 调用 合成 的 destructor 14 27: 89 d8 mov %ebx,%eax 15 29: 83 c4 2c add $0x2c,%esp 16 2c: 5b pop %ebx 17 2d: 89 ec mov %ebp,%esp 18 2f: 5d pop %ebp 19 30: c3 ret
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
3. case 1, 含有 “带有 default constructor 的 member class object”
1 #include <iostream> 2 #include <string> 3 4 class TEST_A 5 { 6 public: 7 std::string s; // s 是带有 default constructor 的 member class object(string 类 具有 default constructor) 8 int a; 9 }; 10 11 int main() 12 { 13 TEST_A test_a; 14 return 0; 15 }
反汇编:由于 string 类 不仅 有 default constructor , 还有 destructor,s 作为 string 类 的对象,又是TEST_A 的成员,故 编译器 为 TEST_A 合成 了 default
constructor,也 合成了 destructor.
1 00000000 <main>: 2 0: 55 push %ebp 3 1: 89 e5 mov %esp,%ebp 4 3: 83 e4 f0 and $0xfffffff0,%esp 5 6: 53 push %ebx 6 7: 83 ec 2c sub $0x2c,%esp 7 a: 8d 44 24 18 lea 0x18(%esp),%eax 8 e: 89 04 24 mov %eax,(%esp) 9 11: e8 fc ff ff ff call 12 <main+0x12> // 调用 编译器 合成的 default constructor 10 16: bb 00 00 00 00 mov $0x0,%ebx 11 1b: 8d 44 24 18 lea 0x18(%esp),%eax 12 1f: 89 04 24 mov %eax,(%esp) 13 22: e8 fc ff ff ff call 23 <main+0x23> // 调用 编译器 合成的 destructor 14 27: 89 d8 mov %ebx,%eax 15 29: 83 c4 2c add $0x2c,%esp 16 2c: 5b pop %ebx 17 2d: 89 ec mov %ebp,%esp 18 2f: 5d pop %ebp 19 30: c3 ret
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
4. case 2,当一个class 派生自 ”带有 default constructor“的 base class
1 #include <iostream> 2 #include <string> 3 4 class TEST_A 5 { 6 public: 7 std::string s; 8 int a; 9 }; 10 class TEST_B:public TEST_A 11 { 12 public: 13 int a; 14 }; 15 16 int main() 17 { 18 TEST_B test_b; 19 return 0; 20 }
反汇编:因为 TEST_A 内含 member class object(此处为 string s),故 编译器 会为它 合成 default constructor 和 destructor;而 TEST_B 又 普通 继承 于
TEST_A , 故编译器 也会为 TEST_B 合成 default constructor 和 destructor
1 00000000 <main>: 2 0: 55 push %ebp 3 1: 89 e5 mov %esp,%ebp 4 3: 83 e4 f0 and $0xfffffff0,%esp 5 6: 53 push %ebx 6 7: 83 ec 2c sub $0x2c,%esp 7 a: 8d 44 24 14 lea 0x14(%esp),%eax 8 e: 89 04 24 mov %eax,(%esp) 9 11: e8 fc ff ff ff call 12 <main+0x12> // 调用 编译器 合成的 default constructor 10 16: bb 00 00 00 00 mov $0x0,%ebx 11 1b: 8d 44 24 14 lea 0x14(%esp),%eax 12 1f: 89 04 24 mov %eax,(%esp) 13 22: e8 fc ff ff ff call 23 <main+0x23> // 调用 编译器 合成的 destructor 14 27: 89 d8 mov %ebx,%eax 15 29: 83 c4 2c add $0x2c,%esp 16 2c: 5b pop %ebx 17 2d: 89 ec mov %ebp,%esp 18 2f: 5d pop %ebp 19 30: c3 ret
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
5. case 3 当一个class 声明(或 继承)一个 virtual function
1 #include <iostream> 2 3 class TEST_A 4 { 5 public: 6 virtual void func(){} 7 int a; 8 }; 9 10 int main() 11 { 12 TEST_A test_a; 13 return 0; 14 }
当一个 class 中声明了 virtual function 时
- 编译器会产生 一个 virtual function table,内放 class 的 virtual functions 地址;
- 在每一个 class object 中,编译器 会 合成 一个 pointer member(即 vptr),用来指向 class 的 vtbl (virtual function table).
对于那些未声明任何 constructor 的 class, 编译器 会为 它们 合成 一个 default constructor, 以便正确的初始化 每一个 class object 的 vptr.
反汇编:TEST_A 未定义任何的 constructor,但又 含有 virtual function,为了使其 对象 能够 找到 类 的virtual functions 的地址,编译器会修改 类的定义,添加
一个 指针成员 (vptr),此时 编译器 还会 合成一个default constructor,用来 初始化 这个 vptr,使其指向 virtual function table.
1 00000000 <main>: 2 0: 55 push %ebp 3 1: 89 e5 mov %esp,%ebp 4 3: 83 e4 f0 and $0xfffffff0,%esp 5 6: 83 ec 20 sub $0x20,%esp 6 9: 8d 44 24 18 lea 0x18(%esp),%eax 7 d: 89 04 24 mov %eax,(%esp) 8 10: e8 fc ff ff ff call 11 <main+0x11> // 调用 编译器 合成 的 default constructor 9 15: b8 00 00 00 00 mov $0x0,%eax 10 1a: c9 leave 11 1b: c3 ret
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
6. case 4 当一个class 派生自一个 继承串链,其中有一个 或 多个 virtual base class
1 #include <iostream> 2 3 class TEST_A 4 { 5 public: 6 int a; 7 }; 8 class TEST_B:public virtual TEST_A 9 { 10 public: 11 int a; 12 }; 13 14 int main() 15 { 16 TEST_B test_b; 17 return 0; 18 }
反汇编: 这个例子中 TEST_A, TEST_B,都没有 定义 default constructor,而且 TEST_A 也不会 被 编译器 合成 default construtor,但是 TEST_B 虚拟继承于TEST_A,从 汇编代码中 看到 在定义一个 TEST_B 对象时,调用了 构造函数,这个 构造函数 就是 编译器 为它 合成的 default constructor.
1 00000000 <main>: 2 0: 55 push %ebp 3 1: 89 e5 mov %esp,%ebp 4 3: 83 e4 f0 and $0xfffffff0,%esp 5 6: 83 ec 20 sub $0x20,%esp 6 9: 8d 44 24 14 lea 0x14(%esp),%eax 7 d: 89 04 24 mov %eax,(%esp) 8 10: e8 fc ff ff ff call 11 <main+0x11> // 调用 编译器 合成的 default constructor 9 15: b8 00 00 00 00 mov $0x0,%eax 10 1a: c9 leave 11 1b: c3 ret
再 看例子 来说明 虚拟继承 时 为什么 要 合成 构造函数: (引用自 《深度探索C++对象模型》 P46)
class X { public: int i; };
class A : public virtual X { public: int j; };
class B : public virtual X { public: double d; };
class C : public A, public B { public: int k; };
// 无法 在 编译期间 解析出 pa->X::i 的位置
void foo ( const A* pa ) { pa->i = 1024;}
main()
{
foo( new A );
foo( new C );
// ...
}
编译器无法固定foo() 中“经由 pa 而存取 X::i " 的 实际偏移位置,因为pa 的 真正类型 可以改变. 编译器 必须 改变 ”执行存取操作“ 的 那些代码,使X::i 可以延迟
到执行期 才决定下来. 所有 ”经由 reference 或 pointer 存取一个 virtual base classes “ 的操作都可以通过 相关指针 完成. 改写 foo()
// 可能的 编译器 转变 操作
void foo( const A * pa ) { pa -> __vbcX -> i = 1024; }
其中 __vbcX 表示 编译器所产生的指针,指向 virtual base class X.
__vbcX 是在 class object 构建 期间被完成的. 对于class 所定义的 每一个 constructor,编译器会 安插 那些 “允许 每一个 virtual base class 到 执行期存取操作”
的代码,如果 class 没有 声明 任何 constructors,编译器 必须 为它合成 一个 default constructor.
===================================================================================================================