2010-03-04 46 views
11

AFAIK,对于指针/引用static_cast,如果类定义此时对编译器不可见,那么static_cast的行为将与reinterpret_cast类似。static_cast safety

为什么static_cast对于指针/引用是不安全的并且对数值是安全的?

回答

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)确实你说的话:如果结构的定义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_castvoid*进行不安全的事情。

+0

多重继承是不是唯一的问题,AndreyT的回答解决向下转换到错误类型的问题。 – 2010-03-04 21:44:12

+0

的确,我只完全回答了问题的第一段,部分是第二段。不是static_cast安全性的广泛问题(标题中)。 – 2010-03-05 11:33:46

5

不,您的“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风格转换可能是一种很好的编程风格,只是为了在始终安全的算术转换和潜在不安全的分层指针/参考转换之间的源代码中提供明显区别。

+0

“使用旧C_style蒙上而不是” - 这就是我平时做太多,但后来有人抱怨,我们结束了争论是否有一个C样式转换,他们说是邪恶的,和单参数的构造函数,他们”之间有什么区别对于;-) – 2010-03-04 19:58:00

+0

非常满意,因此,如果static_cast未针对未定义的指针/引用进行编译,那么对于该类型的转换,它可以被认为是安全的? – dimba 2010-03-04 20:22:44

+0

@idimba:呃...我很难尝试将“安全”和“不安全”的概念应用于无法编译的代码。我甚至会说这没什么意义。 – AnT 2010-03-04 20:24:38