static_cast安全

时间:2010-03-04 19:18:46

标签: c++ casting

AFAIK,对于指针/引用static_cast,如果此时编译器看不到类定义,那么static_cast将表现得像reinterpret_cast

为什么static_cast对指针/引用不安全,对数值是否安全?

2 个答案:

答案 0 :(得分:22)

简而言之,因为多重继承。

长期:

#include <iostream>

struct A { int a; };
struct B { int b; };
struct C : A, B { int c; };

int main() {
    C c;
    std::cout << "C is at : " << (void*)(&c) << "\n";
    std::cout << "B is at : " << (void*)static_cast<B*>(&c) << "\n";
    std::cout << "A is at : " << (void*)static_cast<A*>(&c) << "\n";

}

输出:

C is at : 0x22ccd0
B is at : 0x22ccd4
A is at : 0x22ccd0

请注意,为了正确转换为B *,static_cast必须更改指针值。如果编译器没有C的类定义,那么它就不会知道B是一个基类,它肯定不知道要应用什么偏移量。

但是在没有定义可见的情况下,static_cast的行为与reinterpret_cast不同,它是禁止的:

struct D;
struct E;

int main() {
    E *p1 = 0;
    D *p2 = static_cast<D*>(p1); // doesn't compile
    D *p3 = reinterpret_cast<D*>(p1); // compiles, but isn't very useful
}

一个普通的C风格的演员,(B*)(&c)做你所说的:如果struct C的定义是可见的,显示B是一个基类,那么它与static_cast相同。如果类型只是前向声明的,那么它与reinterpret_cast相同。这是因为它被设计为与C兼容,这意味着它必须在C中可能执行C所做的事情。

static_cast总是知道如何为内置类型做什么,这就是内置的意思。它可以将int转换为float,依此类推。所以这就是数字类型总是安全的原因,但它不能转换指针,除非(a)它知道它们指向的是什么,以及(b)指向类型之间存在正确的关系。因此,它可以将int转换为float,但不能int*转换为float*

正如AndreyT所说,有一种方法可以不安全地使用static_cast,编译器可能不会保存你,因为代码是合法的:

A a;
C *cp = static_cast<C*>(&a); // compiles, undefined behaviour

static_cast可以做的事情之一是“向下转换”指向派生类的指针(在这种情况下,C是A的派生类)。但是如果referand实际上并不是派生类,那你就注定了。 dynamic_cast会在运行时执行检查,但对于我的示例类C,您不能使用dynamic_cast,因为A没有虚函数。

您可以使用static_castvoid*进行不安全的事情。

答案 1 :(得分:5)

不,您的“AFAIK”不正确。 static_cast永远不会表现为reinterpret_cast(除非您转换为void *,但此转换通常不应由reinterpret_cast执行。)

首先,当static_cast用于指针或引用转换时,static_cast的规范明确要求在类型之间存在某种关系(并且static_cast已知)。对于类类型,它们通过继承关联,如static_cast所感知的那样。如果没有两种类型完全由static_cast定义,则无法满足该要求。因此,如果定义({)在static_cast处不可见,则代码将无法编译。

为了举例说明上述内容:static_cast可以[冗余]用于执行对象指针向上转换。代码

Derived *derived = /* whatever */;
Base *base = static_cast<Base *>(derived);

仅在以下代码可编译时才可编辑

Base *base(derived);

为了编译这两种类型的定义必须是可见的。

此外,static_cast可用于执行对象指针向下转换。代码

Base *base = /* whatever */;
Derived *derived = static_cast<Derived *>(base);

仅在以下代码可编译时才可编辑

Base *base(derived); // reverse direction

再次,为了编译这两种类型的定义必须是可见的。

因此,您将无法将static_cast与未定义的类型一起使用。如果您的编译器允许,那么它就是编译器中的错误。

由于完全不同的原因,

static_cast对指针/引用可能不安全。 static_cast可以对对象指针/引用类型执行分层向下转换,而无需检查对象的实际动态类型。 static_cast还可以对方法指针类型执行分层上转换。如果不加注意,使用这些未经检查的强制转换的结果可能会导致未定义的行为。

其次,当static_cast与算术类型一起使用时,语义完全不同 与上述无关。它只执行算术类型转换。只要它们符合您的意图,它们总是非常安全(除了形成范围问题)。实际上,避免static_cast进行算术转换并使用旧的C风格转换可能是一种很好的编程风格,只是为了在始终安全的算术转换和可能不安全的分层之间提供源代码的明显区别。指针/参考演员。