请解释一下
const_cast
dynamic_cast
reinterpret_cast
static_cast
谢谢。
C++ 里最好杜绝使用 C 方式的强制转换, 换用以上 4 个.
我们通常用的是 static_cast
在一类东西都可以转, 但是不是一类的就不能转.
即, 语义上说不通的, 两个完全不同的数据类型 static_cast
是拒绝工作的.
比如你想把一个指针转成浮点数,
或者想把 class A * 转成 class B * , 但是 class A 和 class B
又没有任何关系. 等等....
static_cast 在通过编译后, 空间和时间效率实际等价于 C 方式强制转换.
都是编译时决定的.
dynamic_cast 类似 static_cast, 但是在一颗类继承树上转换时,
将利用 RTTI 在运行时检查. 我们一般用于 downcast
比如,
class A {};
class B : public A {};
A* a=new B();
这个时候, 可以用 dynamic_cast 做 downcast, 把 a 转成 B*.
和 static_cast 不同, dynamic_cast 会检查一下 a 到底是不是指向一个
B, (利用了 RTTI) 如果转不了, 将返回一个 NULL.
reinterpret_cast 就比 static_cast 更接近 C 的强制转换了.
它更进一步的, 实现一些看起来没关系的两种类型转换.
比如我习惯干的, 把一个 void * 转成 unsigned ;)
当然它比 static_cast 危险.
但是有 reinterpret_cast 干不了的,
比如你在一个 const 成员函数里企图修改一个非 mutable 的成员变量.
编译器会对你咆哮, "不许动, 那玩意是我 const 住的, 把你的爪子
收回去" 这个时候就要利用 const_cast 了, 呵呵.
const_cast 就是可以解除 const 限制的"神"的武器
但我认为, 这在很多情况下比 reinterpret_cast 更危险, 我还是老实做
人的好. 让编译器来捍卫我的代码的安全.
这四个cast用来替代C里面包打一切类型转换的.
const_cast用来改变变量的“常数性”,例如:
void func(const int* pcn)
{
int* pn = const_cast<int*>(pcn);
*pn = 10;
}
dynamic_cast用来把指向基类的指针安全地转化为指向派生类的指针,如果基类指针指向的对象不是派生类的对象,那么转换结果为NULL。例如,
CMainFrame* pFrame = dynamic_cast<CMainFrame*>(GetParent());
如果调用GetParent的窗口的父窗口确实是CMainFrame类型,则pFrame指向
该窗口,否则pFrame为NULL。
static_cast也可以加基类的指针转化为派生类的指针,但不做运行是类型检查,所以可以隐含错误。此外,static_cast还可用于显式的类型转化,如将float转化为int,将int转化为double等等。例如:
int i = 3;
double d = static_cast<double>(i);
这样做的好处是避免了编译时关于精度损失的警告信息。
reinterpret_cast是强力类型转换,进行所谓不合理的转换,或者说上面三个操作符无法进行的转换。例如把int转化为指向类的指针:
void OnMessage(WPARAM wParam, LPARAM lParam)
{
CMyClass* pMyStructure
= reinterpret_cast<CMyClass*>(lParam);
...
}
关于这四个运算符,我能说的最后一句话就是,请不要再用cast了,专心使用这四个啰嗦的运算符吧。他们会使你受益的。
* C++提供了四种新的类型强制:
static_cast
const_cast
reinterpret_cast
dynamic_cast
1)staic_cast静态强制;
不能在无关的指针之间进行static类型强制
class CAnimal
{
//...
public:
CAnimal(){}
};
class CGiraffe:public CAnimal
{
//...
public:
CGiraffe(){}
};
int main(void)
{
CAnimal an;
CGiraffe jean;
an = static_cast<CAnimal>(jean);//将对象jean强制成CAnimal类型
return 0;
}
2、const_cast类型强制
const_cast类型强制将一个const变量变成一个非const的等价形式
int main()
{
const int j = 99;
int * k;
k = const_cast<int *>(&j);//解除const
return 0;
}
3、reinterpret_cast运算符
reinterpret_cast运算符用来将一个类型指针转变为另一种类型的指针,也用在将整开型量转为指针,或将指针转为整型量上;
int main()
{
int j = 10;
int * ptr = &j;
char * cptr;
cptr = reinterpret_cast<char *>(ptr);//将int指针类型转变为char的指针类型
return 0;
}
4、dynamic_cast运算符
dynamic_cast的主要目的是:
1)它返回派生类对象的地址;
2)它测试基类指针是否指向下一尖括号<>中所指定类型的对象
dynamic_cast是一个运行时类型信息,dynamic_cast运算符将指向派生对象的基类部分的基类指针转变为指向派生对象的派生类指针,dynamic_cast必须严格地指定与派生对象相同的类,或者它返回NULL指针;
class CAnimal
{
//...
};
class CGiraffe:public CAnimal
{
//...
};
class CGoat:public CAnimal
{
//...
};
int main()
{
CGiraffe gene;
CAnimal * aptr = &gene;
CGiraffe * ptr1,* ptr2;
ptr1 = dynamic_cast<CGiraffe *>(aptr);
ptr2 = dynamic_cast<CGoat *>(aptr); //return NULL
return 0;
}
想不出比此更好的表达,请参阅more effected c++
条款2 尽量使用C++风格的类型转换
仔细想想地位卑贱的类型转换功能 cast 其在程序设计中的地位就象goto 语句一样令人鄙视 但是它还不是无法令人忍受 因为当在某些紧要的关头 类型转换还是必需的 这时它是一个必需品不过C风格的类型转换并不代表所有的类型转换功能 一来它们过于粗鲁 能允许你在任何类型之间进行转换 不过如果要进行更精确的类型转换 这会是一个优点 在这些类型转换中存在着巨大的不同 例如把一个指向const 对象的指针 pointer-to-const-object 转换成指向非const对象的指针 pointer-to-non-const-object
(即一个仅仅去除cosnt 的类型转换) 把一个指向基类的指针转换成指向子类的指针 即完全改变对象类型 传统的C风格的类型转换不对上述两种转换进行区分 这一点也不令人惊讶 因为C风格的类型转换是为C语言设计的 而不是为C++语言设计的 二来C风格的类型转换在程序语句中难以识别 在语法上类型转换由圆括号和标识符组成 而这些可以用在C 中的任何地方 这使得回答象这样一个最基
本的有关类型转换的问题变得很困难 在这个程序中是否使用了类型转换
这是因为人工阅读很可能忽略了类型转换的语句 而利用象grep 的工具程序也
不能从语句构成上区分出它们来
C++通过引进四个新的类型转换操作符克服了C风格类型转换的缺点 这四个操
作符是, static_cast, const_cast, dynamic_cast, 和reinterpret_cast 在大多数情况下 对
于这些操作符你只需要知道原来你习惯于这样写
(type) expression
而现在你总应该这样写
static_cast<type>(expression)
例如 假设你想把一个int 转换成double 以便让包含int 类型变量的表达式产生
出浮点数值的结果 如果用C风格的类型转换 你能这样写
int firstNumber, secondNumber;
...
double result = ((double)firstNumber)/secondNumber
如果用上述新的类型转换方法 你应该这样写
double result = static_cast<double>(firstNumber)/secondNumber;
这样的类型转换不论是对人工还是对程序都很容易识别
static_cast 在功能上基本上与C 风格的类型转换一样强大 含义也一样 它也有
功能上限制 例如 你不能用static_cast 象用C风格的类型转换一样把struct 转换
成int 类型或者把double 类型转换成指针类型 另外 static_cast 不能从表达式中
去除const 属性 因为另一个新的类型转换操作符const_cast 有这样的功能
其它新的C++类型转换操作符被用在需要更多限制的地方 const_cast 用于类型
转换掉表达式的const 或volatileness 属性 通过使用const_cast 你向人们和编译
器强调你通过类型转换想做的只是改变一些东西的constness 或者volatileness 属
性 这个含义被编译器所约束 如果你试图使用const_cast 来完成修改constness 或
者volatileness 属性之外的事情 你的类型转换将被拒绝 下面是一些例子
class Widget { ... };
class SpecialWidget: public Widget { ... };
void update(SpecialWidget *psw);
SpecialWidget sw; // sw 是一个非const 对象
const SpecialWidget& csw = sw; // csw 是sw 的一个引用
// 它是一个const 对象
update(&csw); // 错误!不能传递一个const SpecialWidget* 变量
// 给一个处理SpecialWidget*类型变量的函数
update(const_cast<SpecialWidget*>(&csw));
// 正确 csw 的const 被显示地转换掉
// csw 和sw 两个变量值在update
//函数中能被更新
update((SpecialWidget*)&csw);
// 同上 但用了一个更难识别
//的C 风格的类型转换
Widget *pw = new SpecialWidget;
update(pw); // 错误 pw 的类型是Widget* 但是
// update 函数处理的是SpecialWidget*类型
update(const_cast<SpecialWidget*>(pw));
// 错误 const_cast 仅能被用在影响
// constness or volatileness 的地方上 ,
// 不能用在向继承子类进行类型转换
到目前为止 const_cast 最普通的用途就是转换掉对象的const 属性
第二种特殊的类型转换符是dynamic_cast 它被用于安全地沿着类的继承关系向
下进行类型转换 这就是说 你能用dynamic_cast 把指向基类的指针或引用转换
成指向其派生类或其兄弟类的指针或引用 而且你能知道转换是否成功 失败的
转换将返回空指针 当对指针进行类型转换时 或者抛出异常 当对引用进行类
型转换时
Widget *pw;
...
update(dynamic_cast<SpecialWidget*>(pw));
// 正确 传递给update 函数一个指针
// 是指向变量类型为SpecialWidget 的pw 的指针
// 如果pw 确实指向一个对象,
// 否则传递过去的将使空指针
void updateViaRef(SpecialWidget& rsw);
updateViaRef(dynamic_cast<SpecialWidget&>(*pw));
//正确 传递给updateViaRef 函数
// SpecialWidget pw 指针 如果pw
// 确实指向了某个对象
// 否则将抛出异常
dynamic_casts 在帮助你浏览继承层次上是有限制的 它不能被用于缺乏虚函数的
类型上 参见条款24 也不能用它来转换掉constness:
int firstNumber, secondNumber;
...
double result = dynamic_cast<double>(firstNumber)/secondNumber;
// 错误 没有继承关系
const SpecialWidget sw;
...
update(dynamic_cast<SpecialWidget*>(&sw));
// 错误! dynamic_cast 不能转换
// 掉const
如你想在没有继承关系的类型中进行转换 你可能想到static_cast 如果是为了
去除const 你总得用const_cast
这四个类型转换符中的最后一个是reinterpret_cast 这个操作符被用于的类型转换
的转换结果几乎都是实现时定义 implementation-defined 因此 使用
reinterpret_casts 的代码很难移植
reinterpret_casts 的最普通的用途就是在函数指针类型之间进行转换 例如 假设
你有一个函数指针数组
typedef void (*FuncPtr)(); // FuncPtr is 一个指向函数
// 的指针 该函数没有参数
// 也返回值类型为void
FuncPtr funcPtrArray[10]; // funcPtrArray 是一个能容纳
// 10 个FuncPtrs 指针的数组
让我们假设你希望 因为某些莫名其妙的原因 把一个指向下面函数的指针存入
funcPtrArray 数组
int doSomething();
你不能不经过类型转换而直接去做 因为doSomething 函数对于funcPtrArray 数组
来说有一个错误的类型 在FuncPtrArray 数组里的函数返回值是void 类型 而
doSomething函数返回值是int 类型
funcPtrArray[0] = &doSomething; // 错误 类型不匹配
reinterpret_cast 可以让你迫使编译器以你的方法去看待它们
funcPtrArray[0] = // this compiles
reinterpret_cast<FuncPtr>(&doSomething);
转换函数指针的代码是不可移植的 C++不保证所有的函数指针都被用一样的方
法表示 在一些情况下这样的转换会产生不正确的结果 参见条款31 所以
你应该避免转换函数指针类型 除非你处于着背水一战和尖刀架喉的危急时刻
一把锋利的刀 一把非常锋利的刀
如果你使用的编译器缺乏对新的类型转换方式的支持 你可以用传统的类型转换
方法代替static_cast, const_cast, and reinterpret_cast 也可以用下面的宏替换来模拟
新的类型转换语法
#define static_cast(TYPE,EXPR) ((TYPE)(EXPR))
#define const_cast(TYPE,EXPR) ((TYPE)(EXPR))
#define reinterpret_cast(TYPE,EXPR) ((TYPE)(EXPR))
你可以象这样使用使用
double result = static_cast(double, firstNumber)/secondNumber;
update(const_cast(SpecialWidget*, &sw));
funcPtrArray[0] = reinterpret_cast(FuncPtr, &doSomething);
这些模拟不会象真实的操作符一样安全 但是当你的编译器可以支持新的的类型转换时它们可以简化你把代码升级的过程
没有一个容易的方法来模拟dynamic_cast 的操作 但是很多函数库提供了函数安全地在派生类与基类之间的进行类型转换 如果你没有这些函数而你有必须进行这样的类型转换 你也可以回到C风格的类型转换方法上 但是这样的话你将不能获知类型转换是否失败 当然 你也可以定义一个宏来模拟dynamic_cast 的功能 就象模拟其它的类型转换一样
#define dynamic_cast(TYPE,EXPR) (TYPE)(EXPR)
请记住 这个模拟并不能完全实现dynamic_cast 的功能 它没有办法知道转换是否失败我知道 是的 我知道 新的类型转换操作符不是很美观而且用键盘键入也很麻烦 如果你发现它们看上去实在令人讨厌 C风格的类型转换还可以继续使用并且合法 然而正是因为新的类型转换符缺乏美感才能使它弥补了在含义精确性和可辨认性上的缺点 并且使用新类型转换符的程序更容易被解析 不论是对人工还是对于工具程序 它们允许编译器检测出原来不能发现的错误 这些都是放弃C风格类型转换方法的强有力的理由 还有第三个理由 也许让类型转换符不美观和键入麻烦是一件好事