• C++ 对象模型 默认构造函数


    “default constructor ... 在需要的时候编译器产生出来。“  ------ 《深度探索C++对象模型》,P39(华中科技大学出版)

    -------------------------------------------------------------------------------------------------------------------------------------------------------

    C++ 新手一般有两个常见的误解

    1. 任何 class 如果没有被定义 default constructor,就会被合成出来一个;
    2. 编译器合成出来的 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 时
    1. 编译器会产生 一个 virtual function table,内放 class 的 virtual functions 地址;
    2. 在每一个 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.

    ===================================================================================================================

  • 相关阅读:
    C# 中的委托和事件
    css样式大全(整理版)
    (转)VS2010 快捷键
    委托小例子
    .NET中Cache的使用
    ObjectiveC面向对象编程继承
    ObjectiveC简介
    ObjectiveC面向对象编程实例化对象、构造函数
    MSSql的多表关联的update语句
    ObjectC 与 Java 区别
  • 原文地址:https://www.cnblogs.com/molly/p/3766761.html
Copyright © 2020-2023  润新知