七、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()*/