多态性是否适用于在C中传递的C ++对象引用?

时间:2013-06-19 07:15:23

标签: c++ c polymorphism

我有一个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的指针,因此调用其重写的虚拟方法?

3 个答案:

答案 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) |
+----------------------------+----------------------------+

AB子对象的虚拟指针指向C

现在想象一下,您想要转换为C* B*。当然,收到B*的代码可能不知道C的存在;实际上,它可能是在编写C之前编译的。因此,B*必须指向B对象的C子对象。换句话说,在从C*转换为B*时,必须将A子对象的大小添加到存储在指针中的地址。如果您不这样做,B*实际上将指向A子对象,这显然是错误的。

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

答案 1 :(得分:0)

C ++使用每个类在内存中的虚函数表, 当一个对象是由该特定派生类创建的时候 虚拟表决定调用哪个函数。

所以它的位c ++编译时间加上Runtime魔术:)

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

答案 2 :(得分:0)

简短的回答:是的,这将有效。

为什么:由于Asome_function是在C ++中实现的,所有虚函数调用都会像往常一样在C ++代码中进行,其中包含类定义,并且没有任何神奇之处。在C代码中,只传递不透明指针,C代码永远不能直接调用虚函数,因为它永远不能编译A的定义。