2

当使用使用 C 风格继承的 C API 时(利用 C 结构的标准布局),例如GLib,我们通常使用 C 风格强制转换来向下转换:

struct base_object
{
    int x;
    int y;
    int z;
};

struct derived_object
{
    base_object base;
    int foo;
    int bar;
};

void func(base_object* b)
{
    derived_object* d = (derived_object*) b; /* Downcast */
}


但是,如果我们正在编写使用这样的 C-API 的新 C++ 代码,我们应该继续使用 C 风格的强制转换,还是应该更喜欢 C++ 强制转换?如果是后者,我们应该使用什么类型的 C++ 转换来模拟 C 向下转换?

起初,我认为reinterpret_cast会合适:

derived_object* d = reinterpret_cast<derived_object*>(b);

但是,我总是很谨慎,reinterpret_cast因为 C++ 标准几乎不能保证会发生什么。使用它可能更static_cast安全void*

derived_object* d = static_cast<derived_object*>(static_cast<void*>(b))

当然,这真的很麻烦,让我觉得在这种情况下最好只使用 C 风格的强制转换。

那么这里的最佳实践是什么?

4

4 回答 4

2

但是,我总是对 reinterpret_cast 保持警惕,因为 C++ 标准几乎不能保证会发生什么。

C++ 风格转换的安全性不亚于 C 风格转换,因为 C 风格转换是根据 C++ 风格转换定义的。

5.4.4 执行的转换

— 一个 const_cast (5.2.11),

— 一个 static_cast (5.2.9),

— 一个 static_cast 后跟一个 const_cast,

— reinterpret_cast (5.2.10),或

— reinterpret_cast 后跟 const_cast,

可以使用显式类型转换的强制转换表示法来执行。

[...]

如果转换可以用上面列出的一种以上的方式解释,则使用列表中第一个出现的解释,即使由该解释产生的强制转换是格式错误的。

可悲的答案是您无法避免像您编写的代码中的强制转换,因为编译器对类之间的关系知之甚少。以某种方式,您可能想要重构它(强制转换或类或使用它们的代码)。

底线是:

如果可以,请使用适当的继承。

如果不能,请使用 reinterpret_cast。

于 2013-03-03T16:59:38.433 回答
2

如果您查看 C++ 规范中的 C 风格转换规范,您会发现转换符号是根据其他类型转换运算符 ( dynamic_cast, static_cast, reinterpret_cast, const_cast) 定义的,并且在这种情况下reinterpret_cast使用。

此外,reinterpret_cast提供的保证比您链接到的答案所指示的要多。你关心的是:

§ 9.2/20:指向标准布局结构对象的指针,使用 reinterpret_cast 适当转换,指向其初始成员(或者如果该成员是位字段,则指向它所在的单元),反之亦然。

如果你想使用强制转换符号,我认为显式使用 C++ 类型转换运算符是最好的。但是,您应该为每次转换(使用实现reinterpret_cast)编写一个函数,然后使用它,而不是在代码中乱扔垃圾。

derived_object *downcast_to_derived(base_object *b) {
    return reinterpret_cast<derived_object*>(b);
}
于 2013-03-03T17:13:45.407 回答
2

使用这样的 C-API 的新 C++ 代码

不要以 C 风格编写新的 C++ 代码,它不会利用 C++ 语言的特性,而且它还会强制包装器的用户使用相同的“C”风格。相反,创建一个适当的 C++ 类来包装 C API 接口细节并将它们隐藏在 C++ 类后面。

我们应该继续使用 C 风格的演员表吗

还是我们应该更喜欢 C++ 类型转换

是的,但只有在你必须这样做的时候。

使用 C++ 继承和虚拟访问器函数(可能)。请说明您打算如何在 func 中使用派生对象,这可能会为您提供更好的答案。

如果 func 期望使用派生对象的方法,那么它应该接收派生对象。如果它希望使用 base_object 的方法,但由于指针指向derived_object,因此方法以某种方式发生了变化,那么虚函数就是 C++ 执行此操作的方法。

此外,您希望传递对 func 的引用,而不是指针。

dynamic_cast,需要满足某些条件:

http://www.cplusplus.com/doc/tutorial/typecasting/

如果您只是将 struct ptrs 转换为 struct ptrs 并且您知道自己想要什么,那么 static_cast 或 reinterpret_cast 可能是最好的?

但是,如果您真的对编写 C++ 代码感兴趣,那么强制转换应该是您最后也是最后的手段,因为有更好的模式。我会考虑铸造的两种常见情况是:

  • 您正在与一些将通用基类传递给事件处理程序的事件传递机制进行交互。

  • 你有一个对象容器。容器要求它包含同质类型(即每个元素都包含相同的“事物”),但您希望在容器中存储不同的类型。

于 2013-03-03T16:26:00.017 回答
-1

我想dynamic_cast这正是你想要的。

于 2013-03-03T16:54:46.500 回答