是 C++显式转换真的那么糟糕吗?
目前我对 C++ 的了解比其他任何知识都更具学术性。在我迄今为止的所有阅读中,使用显式转换和命名强制转换(const_cast
、static_cast
、reinterpret_cast
、dynamic_cast
) 带有一个很大的警告标签(很容易看出原因),这意味着显式转换是糟糕设计的症状,只能在绝望的情况下作为最后的手段。所以,我不得不问:
使用命名强制转换的显式转换真的只是陪审团操纵代码,还是对此功能有更优雅和更积极的应用?后者有一个很好的例子吗?
My knowledge of C++ at this point is more academic than anything else. In all my reading thus far, the use of explicit conversion with named casts (const_cast
, static_cast
, reinterpret_cast
, dynamic_cast
) has come with a big warning label (and it's easy to see why) that implies that explicit conversion is symptomatic of bad design and should only be used as a last resort in desperate circumstances. So, I have to ask:
Is explicit conversion with named casts really just jury rigging code or is there a more graceful and positive application to this feature? Is there a good example of the latter?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(6)
在某些情况下,你就是离不开它。就像这个。问题在于您有多重继承,需要将
this
指针转换为void*
,同时确保进入void*
的指针将仍然指向当前对象的右侧子对象。使用显式强制转换是实现这一目标的唯一方法。有一种观点认为,如果你不能没有演员,那么你的设计就很糟糕。我不能完全同意这一点 - 可能存在不同的情况,包括上面提到的一种,但也许如果您需要经常使用显式强制转换,那么您的设计确实很糟糕。
There're cases when you just can't go without it. Like this one. The problem there is that you have multiple inheritance and need to convert
this
pointer tovoid*
while ensuring that the pointer that goes intovoid*
will still point to the right subobject of the current object. Using an explicit cast is the only way to achieve that.There's an opinion that if you can't go without a cast you have bad design. I can't agree with this completely - different situations are possible, including one mentioned above, but perhaps if you need to use explicit casts too often you really have bad design.
在某些情况下,您无法真正避免显式强制转换。特别是在与 C 库或设计不当的 C++ 库(例如用作示例的
COM
库 Sharptooth)交互时。一般来说,使用显式强制转换会分散注意力。它并不一定意味着糟糕的代码,但它确实引起了人们对潜在危险用途的关注。
但是,您不应将 4 个类型转换放在同一个包中:
static_cast
和dynamic_cast
经常用于向上转换(从 Base 到 Derived)或在相关类型之间导航。它们在代码中的出现是很正常的(事实上,如果没有其中任何一个,就很难编写Visitor
模式)。另一方面,使用
const_cast
和reinterpret_cast
则危险得多。const_cast
尝试修改只读对象是未定义的行为(感谢 James McNellis 的纠正)reinterpret_cast
通常仅用于处理原始内存(分配器)当然有它们的用途,但在正常代码中不应该遇到。不过,对于处理外部或 C API,它们可能是必要的。
至少我是这么认为的。
There are situations when you can't really avoid explicit casts. Especially when interacting with C libraries or badly designed C++ libraries (like the
COM
library sharptooth used as examples).In general, the use of explicit casts IS a red herring. It does not necessarily means bad code, but it does attract attention to a potential dangerous use.
However you should not throw the 4 casts in the same bag:
static_cast
anddynamic_cast
are frequently used for up-casting (from Base to Derived) or for navigating between related types. Their occurrence in the code is pretty normal (indeed it's difficult to write aVisitor
Pattern without either).On the other hand, the use of
const_cast
andreinterpret_cast
is much more dangerous.const_cast
to try and modify a read-only object is undefined behavior (thanks to James McNellis for correction)reinterpret_cast
is normally only used to deal with raw memory (allocators)They have their use, of course, but should not be encountered in normal code. For dealing with external or C APIs they might be necessary though.
At least that's my opinion.
演员阵容的糟糕程度通常取决于演员阵容的类型。所有这些石膏都有合法的用途,但有些闻起来比其他的更难闻。
const_cast
用于消除const
性(因为添加它不需要强制转换)。理想情况下,永远不应该使用它。它可以很容易地调用未定义的行为(尝试更改最初指定为 const 的对象),并且在任何情况下都会破坏程序的 const 正确性。有时,当与本身不是const
正确的 API 进行交互时,这是必要的,例如,当他们要将其视为时,可能会要求提供
,但由于您不应该以这种方式编写 API,因此这表明您正在使用非常旧的 API 或者有人搞砸了。char *
>const char *reinterpret_cast
始终依赖于平台,因此在可移植代码中充其量是有问题的。此外,除非您对对象的物理结构进行低级操作,否则它不会保留意义。在 C 和 C++ 中,类型应该是有意义的。int
是一个具有某种意义的数字;基本上是char
的串联的int
并没有真正的意义。dynamic_cast
通常用于向下转型;例如,从Base *
到Derived *
,条件是它要么工作,要么返回 0。这与switch
颠覆了 OO。类型标记上的 code> 语句的作用是:它将定义类的代码移出类定义。这将类定义与其他代码耦合在一起并增加了潜在的维护负担。static_cast
用于已知通常正确的数据转换,例如与void *
之间的转换、类层次结构中已知的安全指针转换等。最糟糕的是它在某种程度上颠覆了类型系统。与 C 库或标准库的 C 部分交互时可能需要它,因为void *
经常在 C 函数中使用。一般来说,精心设计和编写的 C++ 代码将避免上述用例,在某些情况下,因为强制转换的唯一用途是做潜在危险的事情,而在其他情况下,因为这样的代码往往会避免这样的需要转换。 C++ 类型系统通常被认为是值得维护的好东西,而强制转换却颠覆了它。
How bad a cast is typically depends on the type of cast. There are legitimate uses for all of these casts, but some smell worse than others.
const_cast
is used to cast awayconst
ness (since adding it doesn't require a cast). Ideally, that should never be used. It makes it easy to invoke undefined behavior (trying to change an object originally designatedconst
), and in any case breaks theconst
-correctness of the program. It is sometimes necessary when interfacing with APIs that are not themselvesconst
-correct, which may for example ask for achar *
when they're going to treat it asconst char *
, but since you shouldn't write APIs that way it's a sign that you're using a really old API or somebody screwed up.reinterpret_cast
is always going to be platform-dependent, and is therefore at best questionable in portable code. Moreover, unless you're doing low-level operations on the physical structure of objects, it doesn't preserve meaning. In C and C++, a type is supposed to be meaningful. Anint
is a number that means something; anint
that is basically the concatenation ofchar
s doesn't really mean anything.dynamic_cast
is normally used for downcasting; e.g. fromBase *
toDerived *
, with the proviso that either it works or it returns 0. This subverts OO in much the same way as aswitch
statement on a type tag does: it moves the code that defines what a class is away from the class definition. This couples the class definitions with other code and increases the potential maintenance load.static_cast
is used for data conversions that are known to be generally correct, such as conversions to and fromvoid *
, known safe pointer casts within the class hierarchy, that sort of thing. About the worst you can say for it is that it subverts the type system to some extent. It's likely to be needed when interfacing with C libraries, or the C part of the standard library, asvoid *
is often used in C functions.In general, well-designed and well-written C++ code will avoid the use cases above, in some cases because the only use of the cast is to do potentially dangerous things, and in other cases because such code tends to avoid the need for such conversions. The C++ type system is generally seen as a good thing to maintain, and casts subvert it.
在我看来,就像大多数东西一样,它们是工具,有适当的用途,也有不适当的用途。强制转换可能是工具经常被不当使用的一个领域,例如,使用
reinterpret_cast
在int
和指针类型之间进行强制转换(这可能会在同时使用这两种类型的平台上中断)大小不同),或者纯粹作为一种 hack 来const_cast
消除常量性,等等。如果您知道它们的用途和预期用途,那么按照它们的设计目的使用它们绝对没有问题。
IMO, like most things, they're tools, with appropriate uses and inappropriate ones. Casting is probably an area where the tools frequently get used inappropriately, for example, to cast between an
int
and pointer type with areinterpret_cast
(which can break on platforms where the two are different sizes), or toconst_cast
away constness purely as a hack, and so on.If you know what they're for and the intended uses, there's absolutely nothing wrong with using them for what they were designed for.
明确的演员阵容具有讽刺意味。如果开发人员的 C++ 设计技能较差,导致他编写的代码需要大量强制转换,那么该开发人员就没有适当地使用显式强制转换机制,或者根本没有使用显式强制转换机制,并且在代码中乱七八糟地使用了 C 风格的强制转换。
另一方面,了解它们的目的、何时使用它们、何时不使用它们以及替代方案是什么的开发人员,就不会编写需要大量转换的代码!
查看这些转换的更细粒度的变体,例如 boost 转换库中的
polymorphic_cast
,让您了解 C++ 程序员在转换时是多么小心。There is an irony to explicit casts. The developer whose poor C++ design skills lead him to write code requiring a lot of casting is the same developer who doesn't use the explicit casting mechanisms appropriately, or at all, and litters his code with C-style casts.
On the other hand, the developer who understands their purpose, when to use them and when not to, and what the alternatives are, is not writing code that requires much casting!
Check out the more fine-grained variations on these casts, such as
polymorphic_cast
, in the boost conversions library, to give you an idea of just how careful C++ programmers are when it comes to casting.铸件表明您正试图将圆钉放入方孔中。有时这是工作的一部分。但是,如果您对孔和钉子都有一定的控制,最好不要创造这种情况,并且编写铸件应该会促使您问自己是否可以做一些事情,这样会更顺利一些。
Casts are a sign that you're trying to put a round peg in a square hole. Sometimes that's part of the job. But if you have some control over both the hole and the peg, it would be better not create this condition, and writing a cast should trigger you to ask yourself if there was something you could have done so this was a little smoother.