七、C++继承与多态-多重继承的那些坑该怎么填

七、C++继承与多态-多重继承的那些坑该怎么填

理解虚基类和虚继承

多重继承:代码复用,一个派生类有多个基类。如:class C: public A,public B{};

虚基类:virtual可以修饰继承方式,是虚继承,被虚继承的类,称作虚基类。class A:virtual public B{};

虚继承的类中会多一个vbptr指向vbtable,Vbtable中保存的是虚基类中数据在派生类中的内存偏移量,从虚基类中继承的成员变量会被放在派生类内存的最下端。

虚函数和虚基类在调用的时候是没有问题的,

但是在delete的时候会发生堆报错

原因是:基类指针类型的成员p指向派生类对象,永远指向的是派生类基类部分数据的起始地址,这里的基类A的起始位置就是vfptr。但是这里的派生类B是虚继承的A,虚继承的部分会放到派生类内存的后面,p指向的就是派生类后面的内存,这种情况Delete p就不会删除派生类中的内存,造成了上图中的问题。

class A{
public:
    virtual void func(){cout<<"call A:func"<<endl;}
    void operator delete (void* ptr){
        cout<<"operator delete:"<<ptr<<endl;
        free(ptr);
    }
private:
    int ma;
};
class B:virtual public A{
public:
    void func(){cout<<"call B:func"<<endl;}
    void* operator new (size_t size){
        void *p= malloc(size);
        cout<<"operator new:"<<p<<endl;
        return p;
    }
private:
    int mb;
};
int main(){
    A *p=new B();
    cout<<"main:p"<<p<<endl;
    p->func();
    delete p;
}

delete的内存地址与new的内存地址不同,所以会造成问题。

菱形继承问题

继承的样子像菱形,叫菱形继承。类D中会继承两个类A中的成员。尽量避开多重继承。

多重继承的好处:可以做更多代码的复用。

用虚继承解决上面的问题。

class A{
public:
    A(int data):ma(data){
        cout<<"A()"<<endl;
    }
    ~A(){
        cout<<"~A()"<<endl;
    }
protected:
    int ma;
};

class B:public A{
public:
    B(int data):A(data),mb(data){
        cout<<"B()"<<endl;
    }
    ~B(){
        cout<<"~B()"<<endl;
    }
protected:
    int mb;
};

class C:public A{
public:
    C(int data):A(data),mc(data){
        cout<<"C()"<<endl;
    }
    ~C(){
        cout<<"~C()"<<endl;
    }
protected:
    int mc;
};

class D:public B,public C{
public:
    D(int data):B(data),C(data),md(data){
        cout<<"D()"<<endl;
    }
    ~D(){
        cout<<"~D()"<<endl;
    }
protected:
    int md;
};

int main(){
    D d(10);
    return 0;
}
/*
输出
A()
B()
A()
C()
D()
~D()
~C()
~A()
~B()
~A()*/

如果虚继承就会在B和C中构造出vbptr指针,在D中指向类A中的成员变量。

使用虚继承避免继承多次的问题:

class B:virtual public A{//使用虚继承避免菱形继承的问题
public:
    B(int data):A(data),mb(data){
        cout<<"B()"<<endl;
    }
    ~B(){
        cout<<"~B()"<<endl;
    }
protected:
    int mb;
};

class C:virtual public A{
public:
    C(int data):A(data),mc(data){
        cout<<"C()"<<endl;
    }
    ~C(){
        cout<<"~C()"<<endl;
    }
protected:
    int mc;
};

class D:public B,public C{
public:
    D(int data):A(data),B(data),C(data),md(data){
        cout<<"D()"<<endl;
    }
    ~D(){
        cout<<"~D()"<<endl;
    }
protected:
    int md;
};

/*
输出结果:
A()
B()
C()
D()
~D()
~C()
~B()
~A()*/

C++的四种类型转换

const_cast:去掉(指针或者引用)常量属性的一个类型转换

static_cast:提供编译器认为安全的类型转换 基类和派生类可以通过static_cast进行转换

reinterpret_cast:类似于c风格的强制类型转换(不安全)

dynamic_cast:主要用在继承结构中,可以支持RTTI类型识别的上下转换

解释一下dynamic_cast的用法:

class Base{
public:
    virtual void func()=0;
};

class Derive1:public Base{
public:
    void func() override {
        cout<<"Derive1::func()"<<endl;
    }
};

class Derive2:public Base{
public:
    void func() override {
        cout<<"Derive2::func()"<<endl;
    }
    //如果想要在这个类里添加一个新业务
    void funcDerive2(){
        cout<<"Derive2::funcDerive2()"<<endl;
    }
};

/**
 * 外部调用上面两个类的接口
 * @param p
 */
void show(Base* p){
    //dynamic_cast会检查p指针是否指向的是一个Derive2类型的对象
    //如果是,dynamic_cast转换类型成功,返回Derive2对象的地址给dp2;否则返回nullptr
    Derive2 *dp2=dynamic_cast<Derive2*> (p);
    if(dp2!= nullptr){
        dp2->funcDerive2();
    }else
        p->func();
}

int main(){
    Derive1 d1;
    Derive2 d2;
    show(&d1);
    show(&d2);

    return 0;
}

/*
输出结果:
Derive1::func()
Derive2::funcDerive2()*/

hmoban主题是根据ripro二开的主题,极致后台体验,无插件,集成会员系统
自学咖网 » 七、C++继承与多态-多重继承的那些坑该怎么填