c++ 基础知识回顾 继承 继承的本质就是数据的copy

c++ 基础知识笔记

继承

什么是继承 继承就是子类继承父类的成员属性以及方法
继承的本质就是 数据的复制 是编译器帮我们做了很多操作

class Base
{
public:
    Base(){
        cout << "Base 构造函数"<< endl;
    }
    ~Base(){
        cout << "Base 析构函数" << endl;
    }
};
class Sub : public Base
{
public:
    Sub(){
        cout << "Sub 构造函数"<< endl;
    }
    ~Sub(){
        cout << "Sub 析构函数"<
效果

这个时候我们打印出来的 Sub的大小是1
因为它没有任何数据
如果说我们这个时候在Base 和Sub类中各自增加两个int 类型的成员变量 我们再来看看Sub的大小

class Base
{
public:
    Base(){
        cout << "Base 构造函数"<< endl;
    }
    ~Base(){
        cout << "Base 析构函数" << endl;
    }
private:
    int x;
    int y;
};
class Sub : public Base
{
public:
    Sub(){
        cout << "Sub 构造函数"<< endl;
    }
    ~Sub(){
        cout << "Sub 析构函数"<
结果

这个时候我们可以看到结果变成了16
为什么是 16呢 因为Base 与 Sub各自有两个int类型的成员变量 int 类型占用4个字节 所以Sub自己有两个int类型的变量 然后又继承了 Base的 所以这个时候的Sub大小就是 以Base为起始+Sub的大小所以就是16个字节
然后我们在构造函数中给这两个类的各自成员赋上初始值再观察下反汇编的代码


调用构造

构造

我们仔细看 编译器帮我们分配了 Sub类的临时内存
我们仔细看一下ECX 存储的就是 我们定义的Sub类的首地址
大小 正好是16个字节 目前还未初始化 所以使用CC填充
下面就是直接调用了Sub类的构造函数我们单步进去看看


QQ图片20190914155436.png

我们跟到Sub的构造函数中可以很明显的看到 先调用了 基类Base 的构造函数 完成Base的成员初始化
然后再初始化Sub类中自身的成员变量
push        ebp
004018B1   mov         ebp,esp
004018B3   push        0FFh
004018B5   push        offset __ehhandler$??0Sub@@QAE@XZ (00447599)
004018BA   mov         eax,fs:[00000000]
004018C0   push        eax
004018C1   mov         dword ptr fs:[0],esp
004018C8   sub         esp,44h
004018CB   push        ebx
004018CC   push        esi
004018CD   push        edi
004018CE   push        ecx
004018CF   lea         edi,[ebp-50h]
004018D2   mov         ecx,11h
004018D7   mov         eax,0CCCCCCCCh
004018DC   rep stos    dword ptr [edi]
004018DE   pop         ecx
004018DF   mov         dword ptr [ebp-10h],ecx
004018E2   mov         ecx,dword ptr [ebp-10h]
004018E5   call        @ILT+395(Base::Base) (00401190)
004018EA   mov         dword ptr [ebp-4],0
004018F1   push        offset @ILT+215(std::endl) (004010dc)
004018F6   push        offset string "Sub \xb9\xb9\xd4\xec\xba\xaf\xca\xfd" (0046e01c)
004018FB   push        offset std::cout (00479a28)
00401900   call        @ILT+665(std::operator<<) (0040129e)
00401905   add         esp,8
00401908   mov         ecx,eax
0040190A   call        @ILT+500(std::basic_ostream >::operator<<) (004011f9)
0040190F   mov         eax,dword ptr [ebp-10h]
00401912   mov         dword ptr [eax+8],1Eh
00401919   mov         ecx,dword ptr [ebp-10h]
0040191C   mov         dword ptr [ecx+0Ch],28h
00401923   mov         dword ptr [ebp-4],0FFFFFFFFh
0040192A   mov         eax,dword ptr [ebp-10h]
0040192D   mov         ecx,dword ptr [ebp-0Ch]
00401930   mov         dword ptr fs:[0],ecx
00401937   pop         edi
00401938   pop         esi
00401939   pop         ebx
0040193A   add         esp,50h
0040193D   cmp         ebp,esp
0040193F   call        __chkesp (00406f70)
00401944   mov         esp,ebp
00401946   pop         ebp
00401947   ret

这里还可以很明显的看到 首先用 mov dword ptr [ebp-10h],ecx
把我们定义的Sub类的对象的首地址 存放到了 ebp-10h 的一个临时变量中
然后调用完基类Base的构造函数后
0040190F mov eax,dword ptr [ebp-10h]
00401912 mov dword ptr [eax+8],1Eh
00401919 mov ecx,dword ptr [ebp-10h]
0040191C mov dword ptr [ecx+0Ch],28h
对Sub类中的成员 a 、b分别赋值
mov dword ptr [eax+8],1Eh 这一段 可以 = [this+0x8] = 30
mov dword ptr [ecx+0Ch],28h 这一段 可以 = [this+0xc] = 40
有没有疑问说为什么 a的地址跑到了 this+8的位置了?
其实前8个字节已经被Base中的成员变量占用了
我们可以看看内存中的数据


QQ图片20190914155436.png

可以看到 this+0 与 this+4 的位置分别存放了 十进制 的10、20的整数值
而this +8 与 this +0xc 的位置 分别存放了 十进制的 30 、40的整数值
到了这里 大家应该明白了 其实继承就是 数据的复制这个说法了吧 其实就是编译器帮我们自动生成了很多我们看不到的代码
现在看到这里 我提出一个小问题 怎么修改 Base中的x 和y的值呢?


QQ图片20190914155436.png

相信聪明的你已经懂了
QQ图片20190914155436.png

你可能感兴趣的:(c++ 基础知识回顾 继承 继承的本质就是数据的copy)