object-model3

  • C++对象模型系列之3

39 继承体系下的对象构造

  • 对象的构造顺序

    • 1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      30
      31
      32
      33
      34
      35
      36
      37
      38
      39
      40
      41
      42
      43
      44
      45
      46
      47
      48
      49
      50
      51
      52
      53
      54
      55
      56
      57
      58
      59
      60
      	class A {
      public:
      A()
      {
      printf("A this= %p \n", this);
      cout << "A::A()"<< endl;
      }
      virtual ~A()
      {
      cout << "A::~A()"<< endl;
      }
      };

      class B :public A{
      public:
      B()
      {
      printf("B this= %p \n", this);
      cout << "B::B()" << endl;

      }
      virtual ~B()
      {
      cout << "B::~B()" << endl;
      }
      };

      class C :public B {
      public:
      static const int NUMS= 5;
      int scores[NUMS];

      enum {NUMS2=10};
      int scores2[NUMS2];

      C() {
      printf("C this= %p \n",this);
      cout << "C::C()"<< endl;
      }
      virtual ~C()
      {
      cout << "C::~C()" << endl;
      }
      };

      void func()
      {
      C cobj;
      }
      /*
      A this= 0073F6A0
      A::A()
      B this= 0073F6A0
      B::B()
      C this= 0073F6A0
      C::C()
      C::~C()
      B::~B()
      A::~A()
      */
    • 先构造内部父类,在构造外部子类,

    • 先析构外部子类,再析构内部父类

    • 初始化列表在父类构造完毕后,再执行

  • 虚函数的特性

    • A B C 三者首地址相同

    • 在A的构造函数中,给C的虚函数表指针赋值A的虚函数表地址

    • 1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      00401F03  mov         eax,dword ptr [this]  
      00401F06 mov dword ptr [eax],offset ns1::A::`vftable' (0409B34h)
      printf("A this= %p \n", this);
      00401F0C mov eax,dword ptr [this]
      00401F0F push eax
      00401F10 push offset string "A this= %p \n" (0409C40h)
      00401F15 call _printf (0401447h)
      00401F1A add esp,8
      /*
      A this= 00D6F9F4
      0x00D6F9F4 34 9b 40 00// A 首地址 四个字节 为A::vftable 表首地址
      */

      00401FB2 mov ecx,dword ptr [this]
      00401FB5 call ns1::A::A (04013EDh)
      00401FBA mov dword ptr [ebp-4],0
      00401FC1 mov eax,dword ptr [this]
      00401FC4 mov dword ptr [eax],offset ns1::B::`vftable' (0409B60h)
      printf("B this= %p \n", this);
      //B中构造函数 继续讲 B::vftable 赋值 B的头四个字节
      //0x00D6F9F4 60 9b 40 00

      004020A4 mov dword ptr [eax],offset ns1::C::`vftable' (0409B88h)
      printf("C this= %p \n",this);
      //C中构造函数 继续将 C::vftable 赋值 C的头四个字节
      //0x00D6F9F4 88 9b 40 00
  • 构造函数对虚函数的调用

    • 某类构造函数调用虚函数,不走虚函数表,直接通过虚函数表静态调用,当前类有则C,否则依次往上找B,找A

    • 1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      30
      C() {
      myvirfunc();//执行时,C还没构造出来 直接调用C虚函数
      printf("C this= %p \n",this);
      cout << "C::C()"<< endl;
      }
      virtual ~C()
      {
      cout << "C::~C()" << endl;
      }
      virtual void myvirfunc()
      {
      myvirfunc2();//在一个虚函数中调虚函数,第二个间接调虚函数走虚函数表
      }
      virtual void myvirfunc2()
      {}
      /*
      myvirfunc();//执行时,C还没构造出来 直接调用C虚函数
      00FF223A mov ecx,dword ptr [this]
      00FF223D call ns1::C::myvirfunc (0FF128Ah)

      myvirfunc2()
      00FD2F33 mov eax,dword ptr [this]
      00FD2F36 mov edx,dword ptr [eax]
      00FD2F38 mov esi,esp
      00FD2F3A mov ecx,dword ptr [this]
      00FD2F3D mov eax,dword ptr [edx+8]
      00FD2F40 call eax
      00FD2F42 cmp esi,esp
      00FD2F44 call __RTC_CheckEsp (0FD11C7h)
      */

40 对象复制、析构函数语义学

  • 对象的默认复制

    • 如果不写自己的拷贝构造,拷贝赋值,编译器也会有默认的对象拷贝,对象赋值(值拷贝!!!)
  • 拷贝赋值运算符、拷贝构造函数

    • 当定义 任何一个构造函数后,编译器就不提供缺省构造函数
  • 禁止对象的拷贝构造和赋值

    • 拷贝构造和拷贝赋值 private,只声明不定义
  • 析构函数语义

    • 1
      dumpbin /all main.obj > main.txt
    • 析构函数合成

      • 如果继承一个基类,并且基类中带析构函数,编译器就合成默认析构函数

      • 1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        11
        12
        13
        14
        15
        16
        17
        18
        class AA {
        public:
        virtual ~AA()
        {
        cout << "AA::~AA()"<< endl;
        }
        };
        class A:public AA {
        public:
        };
        void func()
        {
        A a;
        }
        }
        /*
        COMDAT; sym= "public: virtual __thiscall ns2::A::~A(void)" (??1A@ns2@@UAE@XZ)
        */
      • 如果类成员变量为类类型成员,该类成员变量有析构函数

        • 1
          2
          3
          4
          5
          6
          7
          8
          9
          10
          11
          12
          13
          14
          15
          16
          17
          18
          19
          20
          21
          22
          	class AA {

          public:
          virtual ~AA()
          {
          cout << "AA::~AA()"<< endl;
          }
          };
          class A:public AA{
          public:

          };

          void func()
          {
          A a;
          }
          /*
          60501020 flags
          Code
          COMDAT; sym= "public: virtual __thiscall ns2::A::~A(void)" (??1A@ns2@@UAE@XZ)
          */
    • 析构函数扩展—-当有自己的析构函数时,编译器扩展

      • 如果类成员为类类型成员,且带析构函数。先执行外部A的析构函数,再执行内部AA的析构函数
      • 基类带析构函数,子类的析构函数被扩展以调用基类的析构函数
文章目录
  1. 1. 39 继承体系下的对象构造
  2. 2. 40 对象复制、析构函数语义学