5

我有一个 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参数 ofsome_function实际上是指向 B 的指针,并因此调用其重写的虚方法?

4

3 回答 3

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*to转换时B*,必须将子对象的大小A添加到存储在指针中的地址。如果不这样做,B*将实际指向A子对象,这显然是错误的。

现在没有访问 的类定义C,当然无法知道甚至一个A子对象,更不用说它有多大了。因此,如果 的类定义不可用,就不可能进行正确的从C*to转换。B*C

于 2013-06-19T07:31:02.333 回答
0

C++ 使用每个类在内存中的虚函数表,并且当从该特定派生类创建对象时,其虚表决定调用哪个函数。

所以它的位 c++ 编译时间加上运行时魔法:)

http://en.wikipedia.org/wiki/Virtual_method_table

于 2013-06-19T07:20:16.593 回答
0

简短的回答:是的,这会起作用。

为什么:因为Aandsome_function是在 C++ 中实现的,所以所有的虚函数调用都会像往常一样在 C++ 代码中发生,其中包含类定义,并且没有什么神奇之处。在 C 代码中,只有不透明的指针被传递,C 代码永远无法直接调用虚函数,因为它永远无法编译A.

于 2013-06-19T07:27:14.760 回答