如果一个类继承自两个基类,比如C继承自A、B,若取类C的基类B的指针pb,那么使用dynamic_cast应该还是可以将pb转回指向类C的指针的,也就是以下代码成立:
class C : public A, public B
{
};
C c;
B *pb;
pb = &c;
assert((void *) pb != (void *) &c);
assert(dynamic_cast
之所以说pb可以转换让我值得琢磨,是因为C的基类A指针具有如此特性,那是非常容易理解的,如例:
A *pa;
pa = &c;
assert((void *) pa == (void *) &c);
assert(dynamic_cast
也就是说pa指针从数值上等于&c,这样pa指向的类使用的vtable就是类C的vtable。而pb显然不等于pa,一般也不等于&c(至于是pa等于&c,还是pb等于&c,这点也许是编译器的特性,我不能肯定C++的标准是如何定义的,但是gcc和msvc都是让pa等于&c)。也就是说pb使用的vtable不是类c的vtable。
倘若pb使用的是类B的vtable,那么dynamic_cast如何知道pb实际上是一个指向C的基类的指针呢?确切的说,它不可能知道,而dynamic_cast又能正常的工作,那么也就是说,pb使用的vtable,绝非是类B的vtable,于是我做了一下实验以确认:
#include <stdio.h>
class A
{
public:
int a;
virtual ~A() {};
};
class B
{
public:
int b;
virtual ~B() {};
};
class C : public A, public B
{
public:
int c;
};
class D : public C
{
public:
int d;
};
int main(int argc, char *argv[])
{
B b;
C c;
D d;
B *pb1, *pb2, *pb3;
C *pc;
pb1 = &b;
pb2 = &c;
pb3 = &d;
pc = &c;
printf("pb1.vtable = %p\npb2.vtable = %p\npb3.vtable = %p\npc.vtable = %p\n",
*(int *) pb1, *(int *) pb2, *(int *) pb3, *(int *) pc);
return 0;
}
根据结果,同样是类B的vtable,原始B类,在C中的基类B,在D中的基类B,其vtable都是不同的。
这意味我们在定义基类时,需要关注一点,每一次派生,都会生成相当数量的vtable(具体数量看其直接、间接继承的基类数量)。
我觉得类继承机制是C++语言的一个非常糟糕的机制,可能是第二差劲的语言特性。继承过程混淆不清,而且隐藏了很多指针变换的操作,不易理解,而且也非常的危险。
我更倾向于类似Java那样的接口机制。如果可以重新设计C++语言的话,我会支持这样做:
- 一个类可以继承若干接口
- 类实例有一个接口类列表,指向所有的接口类;每个接口类实力有一个owner指针,指向派生类
- 调用时自动选择相应的接口类,如果重名,需要指明名字空间
没有评论:
发表评论