class A{
public:
A(){ptr= "abcdefg";};
~A(){};
virtualvoid print(){
printf("%s\r\n",ptr);
}
virtualvoid printA(){
printf("A::%s\r\n",ptr);
}
public:
char*ptr;
};
class B:virtual public A{
public:
B(){ptr= "BBBBBBBBBBBBBBB";bv = 0xbbbbbbbb;}
~B(){};
virtualvoid print(){
printf("B::Print[%s]\r\n",ptr);
}
virtualvoid printB(){
printf("B::Print1[%s]\r\n",ptr);
}
public:
intbv;
};
class C:virtual public A{
public:
C(){ptr= "CCCCCCCCCCCCCCCCCCC";cv =0xcccccccc;}
~C(){};
virtualvoid print(){
printf("C::[%s][%s]\r\n",ptr,ptr);
}
virtualvoid printC(){
printf("C::[%s][%s]\r\n",ptr,ptr);
}
private:
intcv;
};
class D:public B,public C{
public:
D(){v=0xdddddddd;}
~D(){};
virtualvoid print(){
printf("D::[%s][%s]\r\n",A::ptr,B::ptr);
}
private:
intv;
};
上面例子是虚继承那么D的内存布局
B::Vfptr |
D相对于B建立的虚函数表,其中不包括A虚函数 |
B::BToAOfsetPtr |
B对象开始到A对象实体的偏移值的地址(B::BToAOfsetPtr+4) |
B::MemberVerb |
B成员变量 |
C::Vfptr |
D相对于C建立的虚函数表,其中不包括A虚函数 |
C::CToAOfsetPtr |
B对象开始到A对象实体的偏移值的地址(C::CToAOfsetPtr+4) |
C::MemberVerb |
C成员变量 |
D::MemberVerb |
D成员变量 |
00000000 |
分隔 |
A:Vfptr |
D相对于A建立的虚函数表 |
A::MemberVerb |
A成员变量 |
注:在B和C对A覆盖的函数在D中一定要覆盖否则编译提示声明不确定;子对象实体到A对象实体的偏移地址要加4,那里的内容才是正确的。
class A{
public:
A(){ptr= "abcdefg";};
~A(){};
virtualvoid print(){
printf("%s\r\n",ptr);
}
virtualvoid printA(){
printf("A::%s\r\n",ptr);
}
public:
char*ptr;
};
class B {
public:
B(){ptr= "BBBBBBBBBBBBBBB"; }
~B(){};
virtualvoid print(){
printf("B::Print[%s]\r\n",ptr);
}
virtualvoid printB(){
printf("B::Print1[%s]\r\n",ptr);
}
public:
char*ptr;
};
class C: public A, public B{
public:
C(){cv=0xcccccccc;}
~C(){};
virtualvoid print(){
printf("C::[%s][%s]\r\n",A::ptr,B::ptr);
}
virtualvoid printC(){
printf("C::[%s][%s]\r\n",A::ptr,B::ptr);
}
private:
intcv;
};
class D:public B,public C{
public:
D(){v=0xdddddddd;}
~D(){};
virtualvoid print(){
printf("D::[%s][%s]\r\n",A::ptr,B::ptr);
}
private:
intv;
};
上面例子是虚继承那么D的内存布局:
A::Vfptr |
D相对于A,C建立的虚函数表 |
A::MemberVerb |
A成员变量 |
B::Vfptr |
D相对于B,C建立的虚函数表 |
B::MemberVerb |
B成员变量 |
C::MemberVerb |
C成员变量 |
D::MemberVerb |
D成员变量 |
注:当多个父类有同名成员时要指名作用域
关于析构:
类的析构函数要重设自己的虚拟函数表指针,是为了它被其它类继承时它作为一个父类,子类析构调用完后再调用它时可以屏蔽子类对自己的覆盖;因为这时子类部分的资源已经被释放,如果不重设虚拟函数表那么析构函数调用的函数如果被子类覆盖,则会调用子类的函数而不是它本身的。