我是否正确地假设C风格的演员(不鼓励)只不过是reinterpret_casts?使用后者在寻找令人讨厌的演员阵容时视觉上引人注目且易于搜索,因此推荐使用C风格的演员阵容?
如果使用const_cast抛弃const并且未定义写入原始const对象,const_cast的目的是什么?
注意:我知道Bjarne正确地谴责他们不安全的施法操作,甚至指出“一个丑陋的操作应该有一个丑陋的句法形式。 “因此C ++中的转换操作符的冗长程度。所以我会尽量减少他们的使用量。诺言。 :)
答案 0 :(得分:5)
没有。 C cast可以相当于const_cast
,static_cast
,reinterpret_cast
或其组合。如果还不够,它还可以做至少一个小伎俩, no 组合的新演员阵容完全可以做到!
如果原始变量的定义没有const_cast
,则可以将const
与定义的结果一起使用,但您拥有的只是const
指针或对该对象的引用。 OTOH,如果你认为你有充分的理由使用const_cast
,那么你应该真的要查找mutable
。
编辑:我想我应该立刻说出来,但是C风格的演员阵容可以转换为难以接近的基类。例如,考虑以下内容:
[编辑:我正在将代码更新为可以编译并且(通常)演示问题的东西。 ]
#include <iostream>
class base1 {
public:
virtual void print() { std::cout << "base 1\n"; }
};
class base2 {
public:
virtual void print() { std::cout << "base 2\n"; }
};
class derived : base1, base2 {}; // note: private inheritance
int main() {
derived *d = new derived;
base1 *b1 = (base1 *)d; // allowed
b1->print(); // prints "base 1"
base2 *b2 = (base2 *)d; // also allowed
b2->print(); // prints "base 2"
// base1 *bb1 = static_cast<base *>(d); // not allowed: base is inaccessible
// Using `reinterpret_cast` allows the code to compile.
// Unfortunately the result is different, and normally won't work.
base1 *bb2 = reinterpret_cast<base1 *>(d);
bb2->print(); // may cause nasal demons.
base2 *bb3 = reinterpret_cast<base2 *>(d);
bb3->print(); // likewise
return 0;
}
使用reinterpret_cast
的代码将编译 - 但尝试使用结果(至少两个中的一个)将导致一个主要问题。 reinterpret_cast
获取派生对象的 base 地址,并尝试将其视为指定类型的基础对象 - 并且因为(最多)一个基础对象实际上可以存在在那个地址,尝试将其视为另一个可以/将导致重大问题。编辑:在这种情况下,除了打印的内容之外,这些类基本相同,所以尽管可以发生任何事情,但对于大多数编译器,最后两个都将打印出“base 1”。 reinterpret_cast接受在该地址发生的任何事情,并尝试将其用作指定的类型。在这种情况下,我(试图)做一些无害但可见的事情。在实际代码中,结果可能不会那么漂亮。
如果代码使用公共继承而不是私有,C样式转换将像static_cast一样工作 - 即它知道派生类中每个基类对象“生活”的位置,并调整结果,因此每个结果指针将起作用,因为它已被调整为指向正确的位置。
答案 1 :(得分:1)
不,C风格的演员表可以充当reinterpret_cast
,const-cast
或static_cast
s,具体视情况而定。这就是为什么不鼓励他们 - 你在代码中看到C风格的演员,需要查找细节以了解它将做什么。例如:
const char* source;
int* target = (int*)source;// - acts as const_cast and reinterpret_cast at once
//int* target = retinterpret_cast<int*>source;// - won't compile - can't remove const
答案 2 :(得分:1)
请记住,const cast可能会对原始标识符以外的其他内容起作用:
void doit(const std::string &cs)
{
std::string &ms = const_cast<std::string &>(cs);
}
int main()
{
std::string s;
doit(s);
}
因此,当doit抛弃const时,在此示例中,底层字符串不是const,因此没有未定义的行为。
<强>更新强>
好的,这是一个更好的例子,说明使用const_cast并非完全没用。我们从一个带有虚函数的基类开始,该函数采用const参数:
class base
{
public:
virtual void doit(const std::string &str);
};
现在你要覆盖那个虚函数。
class child : public base
{
public:
virtual void doit(const std::string &str)
{
std::string &mstr = const_cast<std::string &>(str);
}
};
由于代码的逻辑/结构,您知道只会使用非常量字符串调用child::doit
(并且class base
不在您的控制之下,因此您无法修改它也不能你可以更改child::doit
的签名,因为它不会再覆盖base::doit
)。在这种情况下,抛弃const是安全的。
是的,这有风险。也许当你写这个时,执行永远不会使用非const字符串到达child::doit
且代码有效。但是,这可能会在维护您的程序时发生变化,也可能在您重建并获取最新版本的class base
时发生变化。
答案 3 :(得分:0)
const_cast
用于从类型中删除const
。它也可以删除volatile
。如果对象确实是const
,则结果无法写入并仍然是明确定义的行为。但是,如果它被提升为const
(通过传递到const T
函数,则const_cast
将其返回到非const
即可。(我发现了一些更多信息here)
reinterpret_cast
无法从某个类型中移除const
或volatile
。
答案 4 :(得分:0)
C风格的演员阵容实际上是编程的大锤 - 你基本上告诉编译器,无论如何,那里的方形钉都将适合这个圆孔。从这个意义上说,reinterpret_cast
非常相似。
我在使用C ++样式的强制转换操作符时看到的主要优点是它们允许您更好地表达您的意图,并允许编译器仍然对您要求它执行的操作进行一些检查而不是尺寸适合所有风格的C演员。
关于const_cast
- 您经常遇到通过const引用传递对象的情况,因为API要求您这样做。比如说,你有一个函数X来执行一个C风格的字符串:
void X(const char *str) { ... }
在该函数内部,您将参数传递给期望char *
的C函数,即使它没有更改字符串。容纳这一点的唯一方法是const_cast
str。
我会非常小心使用任何类型的演员表,通常这表明你的设计有些不太正确,但有时候你必须说服编译器说它所看到的挂钩并不像它假设的那样正方形。只有这样才能使用强制转换操作符。