经常转换与static_cast vs. dynamic_cast
我已经写了C和C ++代码近二十年了,但是这些语言的一个方面我从来没有真正理解。 我显然使用了常规演员,即
MyClass *m = (MyClass *)ptr;
到处都是,但似乎还有两种types的演员,我不知道区别。 以下几行代码有什么区别?
MyClass *m = (MyClass *)ptr; MyClass *m = static_cast<MyClass *>(ptr); MyClass *m = dynamic_cast<MyClass *>(ptr);
的static_cast
static_cast
用于基本上想要反转隐式转换的情况,只有一些限制和附加。 static_cast
执行运行时检查。 如果你知道你指的是一个特定types的对象,那么这个应该被使用,因此检查是不必要的。 例:
void func(void *data) { // Conversion from MyClass* -> void* is implicit MyClass *c = static_cast<MyClass*>(data); ... } int main() { MyClass c; start_thread(&func, &c) // func(&c) will be called .join(); }
在这个例子中,你知道你传递了一个MyClass
对象,因此不需要运行时检查来确保这一点。
的dynamic_cast
当你不知道对象的dynamictypes是什么时, dynamic_cast
是有用的。 如果引用的对象不包含作为基类转换的types(当您转换为引用时,在此情况下引发bad_cast
exception),它将返回空指针。
if (JumpStm *j = dynamic_cast<JumpStm*>(&stm)) { ... } else if (ExprStm *e = dynamic_cast<ExprStm*>(&stm)) { ... }
如果向下转换(转换为派生类)并且参数types不是多态,则不能使用dynamic_cast
。 例如,以下代码无效,因为Base
不包含任何虚函数:
struct Base { }; struct Derived : Base { }; int main() { Derived d; Base *b = &d; dynamic_cast<Derived*>(b); // Invalid }
“向上”( static_cast
转换为基类)对于static_cast
和dynamic_cast
都是有效的,并且也没有任何static_cast
转换,因为“向上转换”是隐式转换。
定期演员
这些演员也被称为C型演员。 C风格的转换基本上与尝试一系列C ++转换序列相同,并且可以在不考虑dynamic_cast
情况下采用第一个可以工作的C ++转换。 毋庸置疑,这是更强大,因为它结合了const_cast
, static_cast
和reinterpret_cast
,但它也是不安全的,因为它不使用dynamic_cast
。
另外,C风格的转换不仅允许你这样做,而且还允许你安全地转换到一个私有的基类,而“等效的” static_cast
序列会给你一个编译时错误。
有些人更喜欢C风格的演员,因为他们简洁。 我只使用它们进行数字转换,并在涉及用户定义types时使用适当的C ++types转换,因为它们提供更严格的检查。
静态投射
静态转换执行兼容types之间的转换。 它类似于C风格的演员,但更具限制性。 例如,C风格的转换将允许一个整数指针指向一个字符。
char c = 10; // 1 byte int *p = (int*)&c; // 4 bytes
由于这将导致指向分配内存的1个字节的4字节指针,写入该指针将导致运行时错误或将覆盖一些相邻的内存。
*p = 5; // run-time error: stack corruption
与C风格的转换相比,静态转换将允许编译器检查指针和指针数据types是否兼容,这允许程序员在编译期间捕获这个不正确的指针分配。
int *q = static_cast<int*>(&c); // compile-time error
重新演绎
要强制指针转换,就像C风格转换在后台执行的一样,将使用reinterpret强制转换。
int *r = reinterpret_cast<int*>(&c); // forced conversion
该转换处理某些不相关types之间的转换,例如从一个指针types到另一个不兼容的指针types。 它将简单地执行数据的二进制拷贝而不改变基础位模式。 请注意,这种低级操作的结果是系统特定的,因此不便携。 如果不能完全避免,应该谨慎使用。
dynamic投射
这个仅用于将对象指针和对象引用转换为inheritance层次结构中的其他指针或引用types。 这是通过执行运行时检查指针指向目标types的完整对象来确保指向的对象可以被转换的唯一转换。 对于这个运行时检查是可能的,对象必须是多态的。 也就是说,这个类必须定义或inheritance至less一个虚函数。 这是因为编译器只会为这些对象生成所需的运行时types信息。
dynamic演员的例子
在下面的例子中,MyChild指针使用dynamic转换转换为MyBase指针。 此派生到基础的转换成功,因为子对象包含完整的基础对象。
class MyBase { public: virtual void test() {} }; class MyChild : public MyBase {}; int main() { MyChild *child = new MyChild(); MyBase *base = dynamic_cast<MyBase*>(child); // ok }
下一个示例尝试将MyBase指针转换为MyChild指针。 由于Base对象不包含完整的Child对象,因此该指针转换将失败。 为了表明这一点,dynamic强制转换返回一个空指针。 这提供了一个方便的方法来检查转换在运行时是否成功。
MyBase *base = new MyBase(); MyChild *child = dynamic_cast<MyChild*>(base); if (child == 0) std::cout << "Null pointer returned";
如果引用被转换而不是指针,那么dynamic转换将通过抛出bad_castexception而失败。 这需要使用try-catch语句来处理。
#include <exception> // … try { MyChild &child = dynamic_cast<MyChild&>(*base); } catch(std::bad_cast &e) { std::cout << e.what(); // bad dynamic_cast }
dynamic或静态演员
使用dynamic转换的优点是它允许程序员在运行时检查转换是否成功。 缺点是执行这个检查有一个性能开销。 由于这个原因,在第一个例子中使用静态转换会更好,因为派生到基地的转换永远不会失败。
MyBase *base = static_cast<MyBase*>(child); // ok
但是,在第二个示例中,转换可能成功或失败。 如果MyBase对象包含一个MyBase实例,它将失败,如果它包含一个MyChild实例,它将成功。 在某些情况下,这可能直到运行时才能知道。 在这种情况下,dynamic投射是比静态投射更好的select。
// Succeeds for a MyChild object MyChild *child = dynamic_cast<MyChild*>(base);
如果使用静态转换而不是dynamic转换来执行基于派生的转换,则转换不会失败。 它会返回一个指向一个不完整的对象的指针。 解引用这样一个指针可能会导致运行时错误。
// Allowed, but invalid MyChild *child = static_cast<MyChild*>(base); // Incomplete MyChild object dereferenced (*child);
Const施放
这个主要用来添加或删除一个variables的const修饰符。
const int myConst = 5; int *nonConst = const_cast<int*>(&myConst); // removes const
尽pipeconst转换允许改变常量的值,但这样做仍然是无效的代码,可能会导致运行时错误。 例如,如果该常量位于只读存储器的一部分中,则可能发生这种情况。
*nonConst = 10; // potential run-time error
当一个函数接受一个非常量的指针参数,即使它没有修改指针时,const cast也会被使用。
void print(int *p) { std::cout << *p; }
然后该函数可以通过使用const转换传递一个常量variables。
print(&myConst); // error: cannot convert // const int* to int* print(nonConst); // allowed
来源和更多的解释
你应该看看文章C ++编程/types铸造 。
它包含了所有不同types转换的很好的描述。 以下取自以上链接:
const_cast会
const_cast(expression)const_cast <>()用于添加/删除variables的const(ness)(或volatile)。
的static_cast
static_cast(expression)static_cast <>()用于在整数types之间进行转换。 '如'char-> long,int-> short等
静态转换也用于投射相关types的指针,例如将void *转换为适当的types。
的dynamic_cast
dynamic转换用于在运行时转换指针和引用,通常用于在inheritance链(inheritance层次结构)上向下或向下转换指针或引用。
dynamic_cast的(expression)
目标types必须是指针或引用types,expression式必须计算为指针或引用。 dynamic转换只在expression式引用的对象的types与目标types兼容且基类至less有一个虚拟成员函数时才起作用。 如果不是,并且被转换的expression式的types是一个指针,则返回NULL,如果引用上的dynamic转换失败,则引发bad_castexception。 当它不失败时,dynamic强制转换将目标types的指针或引用返回到expression式引用的对象。
reinterpret_cast的
重新演绎投射简单地将一种types逐位投射到另一种types。 任何指针或整数types都可以通过重新解释来铸造,容易被滥用。 例如,通过重新解释强制转换,可以不安全地将一个整型指针转换为一个string指针。
避免使用C风格的演员。
C风格的转换是const和reinterpret转换的混合,在代码中很难find和replace。 一个C ++应用程序员应该避免C风格的转换。
仅供参考,我相信Bjarne Stroustrup被引用说,C风格的演员是应该避免的,你应该尽可能使用static_cast或dynamic_cast。
Barne Stroustrup的C ++风格FAQ
听取你的build议。 我远不是一个C ++大师。
C风格强制转换const_cast,static_cast和reinterpret_cast。
我希望C ++没有C风格的演员。 C ++强制转换(正如他们应该这样;转换通常表示正在做一些不好的事情),并恰当地区分转换执行的不同types的转换。 他们还允许编写类似外观的函数,例如boost :: lexical_cast,从一致性angular度来看这是相当不错的。
dynamic_cast
具有运行时types检查function,仅适用于引用和指针,而static_cast
不提供运行时types检查。 有关完整信息,请参阅MSDN文章static_cast运算符 。
dynamic_cast
只支持指针和引用types。 如果types是一个指针,那么它将返回NULL
,如果该types是一个指针,则不可能抛出exception,否则返回一个exception。 因此,可以使用dynamic_cast
来检查一个对象是否是给定的types, static_cast
不能(你最终会得到一个无效的值)。
其他答案已经涵盖了C式(和其他)演员。