据我所知,对于指针/引用的static_cast
,如果编译器此时无法看到类的定义,则static_cast
将像reinterpret_cast
一样行事。
static_cast
为什么对于数值类型是安全的,而对于指针/引用不安全呢?
据我所知,对于指针/引用的static_cast
,如果编译器此时无法看到类的定义,则static_cast
将像reinterpret_cast
一样行事。
static_cast
为什么对于数值类型是安全的,而对于指针/引用不安全呢?
#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
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)
做你说的事情:如果结构体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的派生类)的指针。但是,如果被引用的对象实际上并不是派生类,那么你就错了。一个 dynamic_cast
会在运行时执行检查,但是对于我的示例类C,你不能使用 dynamic_cast
,因为A没有虚函数。
同样地,你可以使用 static_cast
对 void*
进行不安全的操作。
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 风格转换可能是一种良好的编程风格,这样可以在源代码中提供清晰的区分,使其区分开始终安全的算术转换和潜在不安全的分层指针/引用转换。