AFAIK,对于指针/引用static_cast,如果类定义此时对编译器不可见,那么static_cast
的行为将与reinterpret_cast
类似。static_cast safety
为什么static_cast
对于指针/引用是不安全的并且对数值是安全的?
AFAIK,对于指针/引用static_cast,如果类定义此时对编译器不可见,那么static_cast
的行为将与reinterpret_cast
类似。static_cast safety
为什么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
注意的是,为了正确地转换为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)
确实你说的话:如果结构的定义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的派生类)。但是,如果说实际上并不是派生类,那么你就注定了。 A dynamic_cast
将在运行时执行检查,但对于我的示例类C,不能使用dynamic_cast
,因为A没有虚拟功能。
您也可以使用static_cast
与void*
进行不安全的事情。
不,您的“AFAIK”不正确。 static_cast
从不表现为reinterpret_cast
(除非可能在您转换为void *
时,虽然此转换通常不应由reinterpret_cast
执行)。
首先,当static_cast
用于指针或引用的转换,static_cast
的说明书中明确要求有一定的关系类型之间存在(并且是已知的static_cast
)。对于班级类型,他们将通过继承关联,如static_cast
所感知的。如果两种类型都没有完全由static_cast
这一点来定义,则无法满足该要求。所以,如果在static_cast
这个点上定义是不可见的,代码根本就不会编译。
为了说明上述与例子:static_cast
可以使用的冗余]来执行对象的指针upcasts。该代码
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风格转换可能是一种很好的编程风格,只是为了在始终安全的算术转换和潜在不安全的分层指针/参考转换之间的源代码中提供明显区别。
多重继承是不是唯一的问题,AndreyT的回答解决向下转换到错误类型的问题。 – 2010-03-04 21:44:12
的确,我只完全回答了问题的第一段,部分是第二段。不是static_cast安全性的广泛问题(标题中)。 – 2010-03-05 11:33:46