重复继承 novirtual+novirtual
B继承A,而C也继承A。 然后D继承B和C。这样就称为重复继承,因为看着像个菱形,所以也叫钻石继承 那么这样继承会出现什么问题呢?我们先从内存分配的角度看。 在VS使用 / d1 reportSingleClassLayoutXXX就能查看内存分配。 具体如下:
运行之后就能看到XXX类这里是VirClass类的分布情况。 好的,切回正题。 假设我们有个这样的代码 正如刚刚那个继承图一样:B继承A,而C也继承A。然后D继承B和C。
#include<iostream>
#include<vector>
using namespace std;
class A {
public:
int x;
int y;
virtual void f1() { cout << "A:f1" << endl; };
virtual void f2() { cout << "A:f2" << endl; };
virtual void f3() { cout << "A:f3" << endl; };
};
class B :public A
{
public:
private:
};
class C :public A
{
public:
private:
};
class DIS :public B, public C {
public:
virtual void f2() { cout << "B:f2" << endl; };
virtual void f3() { cout << "B:f3" << endl; };
};
int main()
{
return 0;
}
别问我为什么D取名为DIS,问就是 / d1 reportSingleClassLayoutXXX的机制打咩 此时的布局是这样的。哦,这样看是不是有点乱,没关系,我给你可视化一波。 并且可以看到class DIS size(24) 说明这个对象有24字节。那么我们来分析一下为什么有24字节 是的,B中会建立一个A同时C也建立了一个A。 由于A对象有虚函数,所以头部会有一个4字节的vfptr[virtual function ptr 虚函数指针] 其次是,int x,y. 8字节。 故一个A对象就有12字节。 由于B创建了一个A对象,C也创建了一个A对象,所以一共是24字节。
由于创建了两个A所以打咩了!!!!这直接就会造成,访问冲突。 举个栗子,
#include<iostream>
#include<vector>
using namespace std;
class A {
public:
int x;
int y;
virtual void f1() { cout << "A:f1" << endl; };
virtual void f2() { cout << "A:f2" << endl; };
virtual void f3() { cout << "A:f3" << endl; };
};
class B :public A
{
public:
void SetX(int x)
{
this->x=x;
}
int GetX1()
{
return x;
}
private:
};
class C :public A
{
public:
int GetX()
{ return x; }
private:
};
class DIS :public B, public C {
public:
virtual void f2() { cout << "B:f2" << endl; };
virtual void f3() { cout << "B:f3" << endl; };
};
int main()
{
DIS* dis = new DIS();
dis->SetX(10);
cout<<dis->GetX()<<endl;
cout << dis->GetX1()<<endl;
return 0;
}
运行结果如下: 啊咧,明明都是访问x,但是为什么一个是0,一个是10? 正是因为这两个x其实是不同的x,一个是B的基类A的x,一个是C的基类A的x。(证明了一波刚刚的内存分配情况合理!)
好的,那么如何化解?
虚继承 virtual+virtual
将继承改成关系改成virtual
#include<iostream>
#include<vector>
using namespace std;
class A {
public:
int x;
int y;
virtual void f1() { cout << "A:f1" << endl; };
virtual void f2() { cout << "A:f2" << endl; };
virtual void f3() { cout << "A:f3" << endl; };
};
class B :virtual public A
{
public:
void SetX(int x)
{
this->x=x;
}
int GetX1()
{
return x;
}
private:
};
class C :virtual public A
{
public:
int GetX()
{ return x; }
private:
};
class DIS : public B,public C {
public:
virtual void f2() { cout << "B:f2" << endl; };
virtual void f3() { cout << "B:f3" << endl; };
};
int main()
{
DIS dis;
dis.SetX(10);
cout<<dis.GetX()<<endl;
cout << dis.GetX1()<<endl;
return 0;
}
运行结果:
这次x都是一样的了 再看内存分配,size(20)字节,舒服,比之前少了。 虚基类指针vbptr和虚函数指针vfptr
仔细看,你会发现B和C里面存放的不是A了,而是vbptr(virtual base ptr 虚继承基类指针) 这个指针指向的就是A。此时B和C共享一个A。 并且这bvptr只占4字节。然后A占12字节。 故4+4+12=20字节
离谱继承 novirtual+virtual
来看个最离谱的。如果C是非virtual而B是virtual会发生什么
#include<iostream>
#include<vector>
using namespace std;
class A {
public:
int x;
int y;
virtual void f1() { cout << "A:f1" << endl; };
virtual void f2() { cout << "A:f2" << endl; };
virtual void f3() { cout << "A:f3" << endl; };
};
class B :virtual public A
{
public:
private:
};
class C :public A
{
public:
private:
};
class DIS : public B,public C {
public:
virtual void f2() { cout << "B:f2" << endl; };
virtual void f3() { cout << "B:f3" << endl; };
};
int main()
{
return 0;
}
雾草,size(28),这波是反向优化了。 好的,那么我们点根烟冷静分析这28是怎么来的。 首先如果是非virtual继承,那么会创造一个A。即B里面会创造一个A 然后如果是virtual继承,则会创建一个vbptr,然后再最外面创建一个A,使得vbptr指向A 好家伙,那就相当于有两个A,并且还多一个vbptr。打咩! 12+12+4=28 所以使用钻石继承的各位务必要小心使用哦。当然像C#,Java这种语言并不支持多重继承所以无需担心,而且类似接口也完全不是那么一回事。
|