我有一个C ++库,它使用像这样的对象层次结构:
class A { ... }
class B : public A { ... }
class C : public A { ... }
我通过typedef和函数通过C API公开功能,如下所示:
#ifdef __cplusplus
typedef A* APtr;
#else
typedef struct A* APtr;
#endif
extern "C" void some_function(APtr obj);
然而,假设使用C API就是这样的:
BPtr b = bptr_create();
some_function((APtr) b);
这是多态有效的,因为B扩展了A,我的API依赖于这样的功能,但是我想确保它仍然可以与C ++代码正确地互操作,即使B覆盖了A的一些虚拟方法。
更重要的是,为什么或为什么不呢? C ++如何在运行时识别obj
的{{1}}参数实际上是指向B的指针,因此调用其重写的虚拟方法?
答案 0 :(得分:7)
C代码无效(在类定义不可见的上下文中也不会有等效的C ++代码),因为在这种情况下C的作用相当于reinterpret_cast
。请注意,在像您这样的简单情况下,它可能会“起作用”,因为大多数编译器会将单个基础对象放在派生对象的开头,因此不需要指针调整。但是,在一般情况下(特别是在使用多重继承时),必须调整指针以指向正确的子对象,并且由于C不知道如何执行此操作,因此强制转换。
那么“指针调整”是什么意思?请考虑以下情况:
class A { virtual ~A(); int i; ... };
class B { virtual ~B(); int j; ... };
class C: public A, public B { ... };
现在C
的布局可能如下:
+----------------------------+----------------------------+
| A subobject (containing i) | B subobject (containing j) |
+----------------------------+----------------------------+
A
和B
子对象的虚拟指针指向C
。
现在想象一下,您想要转换为C*
B*
。当然,收到B*
的代码可能不知道C
的存在;实际上,它可能是在编写C
之前编译的。因此,B*
必须指向B
对象的C
子对象。换句话说,在从C*
转换为B*
时,必须将A
子对象的大小添加到存储在指针中的地址。如果您不这样做,B*
实际上将指向A
子对象,这显然是错误的。
现在无法访问C
的类定义,当然没有办法知道甚至是一个A
子对象,更不用说它有多大了。因此,如果C*
的类定义不可用,则无法从B*
到C
进行正确的转换。
答案 1 :(得分:0)
C ++使用每个类在内存中的虚函数表, 当一个对象是由该特定派生类创建的时候 虚拟表决定调用哪个函数。
所以它的位c ++编译时间加上Runtime魔术:)
答案 2 :(得分:0)
简短的回答:是的,这将有效。
为什么:由于A
和some_function
是在C ++中实现的,所有虚函数调用都会像往常一样在C ++代码中进行,其中包含类定义,并且没有任何神奇之处。在C代码中,只传递不透明指针,C代码永远不能直接调用虚函数,因为它永远不能编译A
的定义。