zoukankan      html  css  js  c++  java
  • C++多重继承

    今天在考虑一个C++实现的时候需要用到多重继承,突然发觉这么多年来基本都没怎么用过多重继承

    只是了解多重继承可以用,不过很容易出问题,尤其是基类是从相同的类派生出来的。

    今天我需要用到的没有这么麻烦,不过我还是对于多重继承的内存释放感到困惑。

    大家应该都知道设计C++类的时候,如果这个类需要被继承,建议把析构函数声明为virtual的。

    之前对于这一点的理解是:

    基类成员的内存释放一般是在基类析构函数里面完成的,如果基类析构函数不是虚函数,派生类析构的时候不会调用基类的,就会导致内存泄露。

    下面是随手写的一个多重继承测试用例

    class Interface1
    {
    public:
        
    ~Interface1()
        {
            printf(
    "~Interface1\n");
        };
        
    virtual void Func1() = 0;
    protected:
        
    int mData1;
    };

    class Interface2
    {
    public:
        
    ~Interface2()
        {
            printf(
    "~Interface2\n");
        };
        
    virtual void Func2() = 0;
    protected:
        
    int mData2;
    };

    class Base
    {
    public:
        
    ~Base()
        {
            printf(
    "~Base\n");
        };
        
    void        Func3()
        {
            printf(
    "Func3\n");
        }
    protected:
        
    int mData;
    };

    class Derived : public Base, public Interface1, public Interface2
    {
    public:
        
    ~Derived()
        {
            printf(
    "~Derived\n");
        };
        
    void Func1()
        {
            printf(
    "Func1\n");
        }

        
    void Func2()
        {
            printf(
    "Func2\n");
        }
    };

    ////////////////////////////////////////////
    // Test case
    ////////////////////////////////////////////
    Derived* d = new Derived();
    Base
    * b = d;
    Interface1
    * i1 = d;
    Interface2
    * i2 = d;
    i1
    ->Func1();
    i2
    ->Func2();
    b
    ->Func3();
    delete i2;

    问题来了,delete i2;这一行会导致Assert(Debug版)

    这是为什么呢?

    先让我们在watch window里面看看这些变量

    i1    0x004e99c0    Interface1 *
    i2    
    0x004e99c8    Interface2 *
    b    
    0x004e99d0    Base *
    d    
    0x004e99c0    Derived *

    Wow,地址竟然都不一样,想想也对,因为虚表的原因。具体的可以参考:这里

    当我把所有析构函数改成virtual的以后,delete就没有问题了。

    这中间发生了什么事情呢?因为i2的地址和我们分配的地址明显是不一样,直接free这块内存的话,当然会出错

    查看正确的析构代码的汇编代码发现窍门在这里

    i2的Interface2虚表中的析构函数指向了下面这个地址

    [thunk]:Derived::`vector deleting destructor':
    004117D0  sub         ecx,10h 
    004117D3  jmp         Interface2::`scalar deleting destructor
    ' (4113C5h) 

    ==>
    004113C5  jmp         Derived::`scalar deleting destructor' (411860h) 

    注意sub ecx, 10h,经过这个变幻以后得到的地址就是我们前面真正的内存地址。

    也就是说在每个基类的虚表中指向的析构函数其实并不是Derived的析构函数地址,而是一个编译器自动生成的中间代码地址,这段代码的功能就是把这个基类的实例内存地址(this指针)转换成内存分配时的真正地址(也就是当时实例化的类型),然后再调用实际类型的析构函数(Derived::~Derived())。

  • 相关阅读:
    Reactive Extensions (Rx) 入门(5) —— Rx的事件编程
    Reactive Extensions (Rx) 入门(4) —— Rx的事件编程
    Reactive Extensions (Rx) 入门(3) —— Rx的事件编程
    Reactive Extensions (Rx) 入门(2) —— 安装 Reactive Extensions
    Reactive Extensions (Rx) 入门(1) —— Reactive Extensions 概要
    Xamarin NuGet 缓存包导致 already added : Landroid/support/annotation/AnimRes 问题解决方案
    Android 系统Action大全
    Xamarin Forms 实现发送通知点击跳转
    如何理解灰度发布
    推荐一款分布式微服务框架 Surging
  • 原文地址:https://www.cnblogs.com/hyamw/p/1871266.html
Copyright © 2011-2022 走看看