AFAIK,对于指针/引用static_cast,如果此时编译器看不到类定义,那么static_cast
将表现得像reinterpret_cast
。
为什么static_cast
对指针/引用不安全,对数值是否安全?
答案 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_cast
与void*
进行不安全的事情。
答案 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风格转换可能是一种很好的编程风格,只是为了在始终安全的算术转换和可能不安全的分层之间提供源代码的明显区别。指针/参考演员。