我在C++中使用多重继承已经有很长一段时间了,但直到今天才意识到,这可能意味着当将指针地址引用为子类之一时,指针地址可能是不同的。
例如,如果我有:
class ClassA{
public:
int x;
int y;
ClassA(){
cout << "ClassA : " << (unsigned int)this << endl;
}
};
class ClassC{
public:
int cc;
int xx;
ClassC(){
cout << "ClassC : " << (unsigned int)this << endl;
}
};
class ClassB : public ClassC, public ClassA{
public:
int z;
int v;
ClassB(){
cout << "ClassB : " << (unsigned int)this << endl;
}
};
int main(){
ClassB * b = new ClassB();
}
类A和类C在构造函数上打印时有不同的地址。
然而,当我试着把它们重新投射到对方身上时,它就会自动地工作:
ClassA * the_a = (ClassA*)b;
cout << "The A, casted : " << (unsigned int)the_a << endl;
ClassB * the_b = (ClassB*)the_a;
cout << "The B, casted back : " << (unsigned int)the_b << endl;
我假设这种信息可以由编译器从代码中派生出来,但是假设这在所有编译器上都起作用是安全的吗?
附加问题:有可能强制子类位置的顺序吗?例如,如果我需要将classA定位在第一位(本质上,共享相同的指针位置)作为它的子类ClassC,那么我是否只需要将它放在子类的声明中的第一位?更新好的,看来不可能强制命令。还有可能在超类级别找出结构的“根”地址,分配给子类的地址的开始吗?例如,从ClassA获取ClassB的地址。
这是多重继承的完美标准使用,它可以在任何兼容的编译器上工作。但是,您应该注意,在第一种情况下不需要显式强制转换,在第二种情况下,可以用
是否有可能强制使用子类位置的顺序。
否:布局是实现定义的,您的代码不应该依赖于这种排序。
是的,你可以认为这是安全的。C++中的指针类型转换可以保证正确地调整指针,以实现从基到派生的转换或从基到派生的转换。
话虽如此,你必须小心,不要把系统推得太远。例如,编译器不能正确地进行此转换:
ClassB* b = new ClassB;
ClassC* c = (ClassC*)(void*)b;
这会中断,因为从C到A的强制转换会通过一个空隙*,因此有关指针在B对象中的位置的信息会丢失。
另一种直接强制转换不起作用的情况是虚拟继承。如果您想从派生类强制转换到虚拟基类,或者相反,我相信您必须使用dynamic_cast运算符来确保强制转换成功。
是的,虚拟基的一个简单实现是在派生对象中的一个已知位置放置一个指向虚拟基子对象的指针。
如果你有多个虚拟基地,那就有点贵了。一个更好的表示方法(我想是微软的专利)使用自相对偏移量。因为它们对于每个子对象都是不变的(也就是说,它们不依赖于对象地址),所以它们可以在静态内存中存储一次,您所需要的只是每个子对象中指向它们的单个指针。
如果没有这样的数据结构,交叉强制转换将无法工作。即使B子对象是不可见的,也可以从虚拟基a交叉转换到虚拟基a内部的虚拟基B(前提是如果我没有记错的话,这两个基只有一个共享的虚拟基X)。当你想起来的时候,这是一个非常复杂的导航:导航通过最派生类的形状描述符(它可以同时看到A和B)。
更令人毛骨悚然的是,这样表现的结构是“标准法则”要求在建造和破坏过程中动态变化的,这就是为什么复杂物体的建造和破坏是缓慢的。然而,一旦进行了方法调用,即使是交叉调用,也是相当快的。