关于C++:正则投射与静态投射与动态投射

Regular cast vs. static_cast vs. dynamic_cast

本问题已经有最佳答案,请猛点这里访问。

我写C和C++代码已经将近二十年了,但是这些语言的一个方面我从来没有真正理解过。很明显,我用的是普通的石膏。

1
MyClass *m = (MyClass *)ptr;

到处都是,但似乎还有另外两种类型的石膏,我不知道有什么区别。以下几行代码有什么区别?

1
2
3
MyClass *m = (MyClass *)ptr;
MyClass *m = static_cast<MyClass *>(ptr);
MyClass *m = dynamic_cast<MyClass *>(ptr);


静态铸造

static_cast用于您基本上想要反向隐式转换的情况,其中有一些限制和补充。static_cast不执行运行时检查。如果您知道您所引用的对象是特定类型的,那么应该使用此选项,这样就不需要进行检查。例子:

1
2
3
4
5
6
7
8
9
10
11
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非常有用。如果引用的对象不包含作为基类强制转换到的类型,则返回空指针(当强制转换到引用时,在这种情况下会引发bad_cast异常)。

1
2
3
4
5
if (JumpStm *j = dynamic_cast<JumpStm*>(&stm)) {
  ...
} else if (ExprStm *e = dynamic_cast<ExprStm*>(&stm)) {
  ...
}

如果向下强制转换(强制转换到派生类),并且参数类型不是多态的,则不能使用dynamic_cast。例如,以下代码无效,因为Base不包含任何虚拟函数:

1
2
3
4
5
6
struct Base { };
struct Derived : Base { };
int main() {
  Derived d; Base *b = &d;
  dynamic_cast<Derived*>(b); // Invalid
}

"向上强制转换"(强制转换为基类)对于static_castdynamic_cast始终有效,并且不进行任何强制转换,"向上强制转换"是一种隐式转换。

规则铸件

这些铸件也称为C型铸件。C样式的CAST基本上与尝试C++序列的一系列序列相同,并且采用第一个C++模型,而不考虑EDCOX1(0)。不用说,它结合了所有的const_caststatic_castreinterpret_cast,但它也不安全,因为它不使用dynamic_cast

此外,C样式的强制转换不仅允许您这样做,而且还允许您安全地强制转换到一个私有的基类,而"等效的"static_cast序列会给您一个编译时错误。

有些人喜欢C型的石膏,因为它们很短。我只使用它们进行数值转换,当涉及到用户定义的类型时,使用适当的C++版本,因为它们提供了更严格的检查。


静态浇铸

静态强制转换执行兼容类型之间的转换。它类似于C样式的强制转换,但更具限制性。例如,C样式的转换将允许一个整数指针指向一个字符。好的。

1
2
char c = 10;       // 1 byte
int *p = (int*)&c; // 4 bytes

由于这会导致一个4字节的指针指向已分配内存的1字节,因此写入该指针将导致运行时错误或覆盖某些相邻内存。好的。

1
*p = 5; // run-time error: stack corruption

与C样式转换不同,静态转换将允许编译器检查指针和指针数据类型是否兼容,这允许程序员在编译期间捕获这种不正确的指针分配。好的。

1
int *q = static_cast<int*>(&c); // compile-time error

重新解释铸造

为了强制进行指针转换,与C样式转换在后台的方式相同,将使用reinterpret转换。好的。

1
int *r = reinterpret_cast<int*>(&c); // forced conversion

此强制转换处理某些不相关类型之间的转换,例如从一个指针类型到另一个不兼容的指针类型。它只执行数据的二进制拷贝,而不改变底层的位模式。请注意,这种低级操作的结果是特定于系统的,因此不可移植。如果不能完全避免,应谨慎使用。好的。动态铸件

此类型仅用于将对象指针和对象引用转换为继承层次结构中的其他指针或引用类型。通过执行运行时检查指针是否引用目标类型的完整对象,它是唯一确保可以转换指向的对象的强制转换。为了使这个运行时检查成为可能,对象必须是多态的。也就是说,类必须定义或继承至少一个虚拟函数。这是因为编译器将只为这些对象生成所需的运行时类型信息。好的。

动态铸造示例好的。

在下面的示例中,使用动态强制转换将mychild指针转换为mybase指针。派生到基的转换成功,因为子对象包含完整的基对象。好的。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
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指针。由于基对象不包含完整的子对象,因此指针转换将失败。为了表明这一点,动态强制转换将返回一个空指针。这为在运行时检查转换是否成功提供了一种方便的方法。好的。

1
2
3
4
5
6
MyBase  *base = new MyBase();
MyChild *child = dynamic_cast<MyChild*>(base);


if (child == 0)
std::cout <<"Null pointer returned";

如果转换引用而不是指针,则动态强制转换将通过引发错误的强制转换异常而失败。这需要使用try catch语句来处理。好的。

1
2
3
4
5
6
7
8
9
10
#include <exception>
// …  
try
{
  MyChild &child = dynamic_cast<MyChild&>(*base);
}
catch(std::bad_cast &e)
{
  std::cout << e.what(); // bad dynamic_cast
}

动态或静态铸造

使用动态强制转换的好处在于,它允许程序员在运行时检查转换是否成功。缺点是,执行此检查会带来性能开销。因此,在第一个示例中,使用静态强制转换更可取,因为派生到基的转换永远不会失败。好的。

1
MyBase *base = static_cast<MyBase*>(child); // ok

但是,在第二个示例中,转换可能成功或失败。如果MyBase对象包含MyBase实例,它将失败;如果它包含MyChild实例,它将成功。在某些情况下,直到运行时才知道这一点。在这种情况下,动态强制转换比静态强制转换更好。好的。

1
2
// Succeeds for a MyChild object
MyChild *child = dynamic_cast<MyChild*>(base);

如果使用静态强制转换而不是动态强制转换来执行基到派生的转换,则转换不会失败。它会返回一个指向不完整对象的指针。取消对此类指针的引用可能会导致运行时错误。好的。

1
2
3
4
5
// Allowed, but invalid
MyChild *child = static_cast<MyChild*>(base);

// Incomplete MyChild object dereferenced
(*child);

连续铸造

这一个主要用于添加或删除变量的const修饰符。好的。

1
2
const int myConst = 5;
int *nonConst = const_cast<int*>(&myConst); // removes const

尽管const cast允许更改常量的值,但这样做仍然是可能导致运行时错误的无效代码。例如,如果常量位于只读内存的一部分,则可能发生这种情况。好的。

1
*nonConst = 10; // potential run-time error

const cast主要在函数接受非常量指针参数时使用,即使它不修改指针。好的。

1
2
3
4
void print(int *p)
{
   std::cout << *p;
}

然后,可以使用const cast向函数传递常量变量。好的。

1
2
3
4
print(&myConst); // error: cannot convert
                 // const int* to int*

print(nonConst); // allowed

来源及更多解释好的。好啊。


你应该看看文章C++编程/类型铸造。

它包含对所有不同类型的转换的良好描述。以下内容摘自上述链接:

const_cast

const_cast(expression) The const_cast<>() is used to add/remove
const(ness) (or volatile-ness) of a variable.

static_cast

static_cast(expression) The static_cast<>() is used to cast between
the integer types. 'e.g.' char->long, int->short etc.

Static cast is also used to cast pointers to related types, for
example casting void* to the appropriate type.

dynamic_cast

Dynamic cast is used to convert pointers and references at run-time,
generally for the purpose of casting a pointer or reference up or down
an inheritance chain (inheritance hierarchy).

dynamic_cast(expression)

The target type must be a pointer or reference type, and the
expression must evaluate to a pointer or reference. Dynamic cast works
only when the type of object to which the expression refers is
compatible with the target type and the base class has at least one
virtual member function. If not, and the type of expression being cast
is a pointer, NULL is returned, if a dynamic cast on a reference
fails, a bad_cast exception is thrown. When it doesn't fail, dynamic
cast returns a pointer or reference of the target type to the object
to which expression referred.

reinterpret_cast

Reinterpret cast simply casts one type bitwise to another. Any pointer
or integral type can be casted to any other with reinterpret cast,
easily allowing for misuse. For instance, with reinterpret cast one
might, unsafely, cast an integer pointer to a string pointer.


仅供参考,我相信bjarne stroustrup是引用说,C风格的强制转换是要避免的,如果可能的话,你应该使用静态强制转换或动态强制转换。

Barne Stroustrup的C++风格常见问题解答

就你所要的接受这个建议。我远非C++大师。


避免使用C型铸造。

C风格的强制转换是const和reinterpret强制转换的混合,在代码中很难找到和替换。C++应用程序程序员应该避免C风格的CAST。


C样式的强制转换将const-cast、static-cast和reinterpret-cast合并在一起。

我希望C++没有C样式的转换。C++正确地(如它们应该)显示出突出;Casts通常表示做坏事)并且正确地区分不同类型的转换。它们还允许编写类似的外观函数,例如boost::lexical_cast,从一致性的角度来看,这非常好。


dynamic_cast只支持指针和引用类型。如果类型是指针,强制转换不可能,则返回NULL;如果类型是引用类型,则返回异常。因此,可以使用dynamic_cast来检查对象是否属于给定类型,而static_cast不能(您只会得到一个无效的值)。

其他答案中包含了C型(和其他)铸件。


dynamic_cast有运行时类型检查,只与引用和指针一起使用,而static_cast不提供运行时类型检查。有关完整信息,请参阅msdn文章static_cast operator。