需要对 C 风格、重新解释和 const 强制转换进行澄清
-
23-09-2019 - |
题
我是否正确地假设 C 风格的强制转换(不鼓励这样做)只不过是 reinterpret_casts?使用后者在视觉上引人注目,并且在寻找令人讨厌的强制转换时易于搜索,因此建议使用它而不是 C 风格的强制转换?
如果使用 const_cast 抛弃 const 并写入原始 const 对象是未定义的,那么 const_cast 的目的是什么?
笔记: 我知道 Bjarne 正确地谴责了选角操作,认为它们是不安全的,甚至达到了“一个 丑陋的 操作应该有一个丑陋的语法形式。”因此 C++ 中的强制转换运算符变得冗长。所以我会尽量减少它们的使用。承诺。:)
解决方案
没有。 A C铸造可以执行相当于const_cast
,一个static_cast
,一个reinterpret_cast
,或它们的组合。在这是不太够的情况下,它也能做到至少一个次要把戏的较新的类型转换没有的组合可以在所有的事!
可以使用与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
s将编译的代码 - 但试图使用的结果(两个中的一个以免的)将导致一个主要问题。所述reinterpret_cast
取碱派生对象并试图把它,如果它是基础对象的指定类型的地址 - 和自(至多)一个基础对象实际上可以在该地址存在,试图把它当作另可/将导致重大问题。编辑:在这种情况下,类是除了他们打印的内容基本相同,所以虽然任何的可能的发生,与大多数编译器,无论是过去两年中会打印出“基地1”。使用reinterpret_cast需要什么恰好是在该地址并尝试用它作为指定的类型。在这种情况下,我(想),使该做些什么无害的,但可见。在实际代码,结果可能不会那么漂亮。
在C样式转换会像将一个的static_cast如果代码已经使用公有继承,而不是私人 - 也就是说,它的了解,其中在派生类中的每个基类对象“生活”,并调整结果的,所以每个所得指针将起作用,因为它是在正确的位置被调节至点。
其他提示
没有,C风格类型转换可以作为根据情况reinterpret_cast
s,const-cast
s或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
记住,可作用于其它东西然后一个const铸造原始标识符
void doit(const std::string &cs)
{
std::string &ms = const_cast<std::string &>(cs);
}
int main()
{
std::string s;
doit(s);
}
因此,虽然DOIT是铸造远常量,在该示例的基本字符串不是常量所以没有未定义的行为。
<强>更新强>
好的,这是一个使用的const_cast时的更好的例子是不是完全没有价值。我们先从与虚拟功能的基类,需要一个常量参数:
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
将只与非const字符串称为(和class base
是不是你的控制之下,所以你不能修改也不能更改child::doit
因为签名那么就不会再覆盖base::doit
)。在这种情况下,就可以安全地抛弃常量。
是,这是危险的。也许当你写的,这是真的,执行将永远不会与非常量字符串达到child::doit
和代码是有效的。但同时保持你的程序或者是当你重建并搭载了最新版本的class base
的可能要么改变。
C-风格转换是真正的编程大锤 - 你基本上告诉编译器,该方挂在那里将适合通过这个圆孔不管。在这个意义上,reinterpret_cast
是非常相似的。
主要优点在我使用C ++见 - 风格的转换符是他们让你更好地表达你的意图,并允许编译器仍然在运行一些检查你问它来执行,而不是单尺寸适合所有C风格铸造。
关于const_cast
-你经常进入情况下,你的周围通过const引用只是传递一个对象,因为API需要你这样做。也就是说,如果你已经有了功能X是任务的C风格的字符串:
void X(const char *str) { ... }
在该功能你传递参数的C函数期望一个char *
,即使它不改变的字符串。以适应这种情况的唯一办法是const_cast
海峡
我会非常小心使用任何类型的演员,通常这表明,有一些不太正确与您的设计,但有时你必须让编译器,它看起来在PEG并不像正方形,因为它假定。只有这样,你应该使用类型转换操作符。