Guru of the week:#17 类型映射.
作者:Hub Sutter
译者:黄森堂
/*此文是译者出于自娱翻译的GotW(Guru of the Week)系列文章的一篇,原文的版权是属于Hub Sutter(著名的C++专家,《Exceptional C++》的作者)。此文的翻译没有征得原作者的同意,只供学习讨论。――译者:黄森堂*/
#17 类型映射.
难度:6/10
你知道C++的类型映射吗?,在你的代码中使用它们来提高可靠性。
问题:
比旧的C的类型映射,在标准C++中新的类型映射提供了更多的安全与存取能力,你知道它们吗?,剩下的问题就是如何使用它们:
class A { /*...*/ }; class B : virtual A { /*...*/ }; struct C : A { /*...*/ }; struct D : B, C { /*...*/ }; A a1; B b1; C c1; D d1; const A a2; const A& ra1 = a1; const A& ra2 = a2; char c;
1.以下新的类型映射没有同等的C的类型映射吗?
const_cast
dynamic_cast
reinterpret_cast
static_cast
2.以下的每一个C的类型映射中,用同等的新的类型映射来写,如果不用新的类型映射来写,它们是错的吗?
void f() { A* pa; B* pb; C* pc; pa = (A*)&ra1; pa = (A*)&a2; pb = (B*)&c1; pc = (C*)&d1; }
3.评价以下C++类型映射后的类型与正确性。
void g() { unsigned char* puc = static_cast<unsigned char*>(&c); signed char* psc = static_cast<signed char*>(&c); void* pv = static_cast<void*>(&b1); B* pb1 = static_cast<B*>(pv); B* pb2 = static_cast<B*>(&b1); A* pa1 = const_cast<A*>(&ra1); A* pa2 = const_cast<A*>(&ra2); B* pb3 = dynamic_cast<B*>(&c1); A* pa3 = dynamic_cast<A*>(&b1); B* pb4 = static_cast<B*>(&d1); D* pd = static_cast<D*>(pb4); pa1 = dynamic_cast<A*>(pb2); pa1 = dynamic_cast<A*>(pb4); C* pc1 = dynamic_cast<C*>(pb4); C& rc1 = dynamic_cast<C&>(*pb2); }
解决方法:
比旧的C的类型映射,在标准C++中新的类型映射提供了更多的安全与存取能力,你知道它们吗?,剩下的问题就是如何使用它们:
class A { /*...*/ }; class B : virtual A { /*...*/ }; struct C : A { /*...*/ }; struct D : B, C { /*...*/ }; A a1; B b1; C c1; D d1; const A a2; const A& ra1 = a1; const A& ra2 = a2; char c;
1.以下新的类型映射有同等的C的类型映射吗?
只有dynamic_cast没用同等的C的类型映射,所有其它的新的类型映射都有相应的旧的类型映射。
2.以下的每一个C的类型映射中,用同等的新的类型映射来写,如果不用新的类型映射来写,它们是错的吗?
void f() { A* pa; B* pb; C* pc; pa = (A*)&ra1;
使用 const_cast: pa = const_cast<A*>(&ra1);
pa = (A*)&a2;
这是错误的表达式,没有同等新的类型映射,const_cast是替代者,但因为a2是const对象,结果不明确。
pb = (B*)&c1;
使用 reinterpret_cast: pb = reinterpret_cast<B*>(&c1);
pc = (C*)&d1; }
上面的类型映射在C是错误的,在C++里,没有映射需要:pc = &d1;
3.评价以下C++类型映射后的类型与正确性。
开始,先声明:我们不知道这些类任何一个是否有虚函数,如果这类不包含虚函数的话,以下所有dynamic_case全是错的,剩下的讨论部分,我们假定所有类都有虚函数,让使用dynamic_cast是合法的,
void g() { unsigned char* puc = static_cast<unsigned char*>(&c); signed char* psc = static_cast<signed char*>(&c);
错误:对以上两行我们必须使用reinterpret_cast,这个首先让你感到惊奇的,但理由是char,singed char与unsigned char是三个不同的类型,在它们之中任何通过对它们进行明确的转换都是没有相联的,所以指向无关的的对象,
void* pv = static_cast<void*>(&b1); B* pb1 = static_cast<B*>(pv);
两者之间是有细微的地方,但前者不是必须的,因为它总是明确地从对象指针向void*指针进行转换。
B* pb2 = static_cast<B*>(&b1);
这儿有细微的地方,但不必要的,因为参数已经是B*.
This is fine, but unnecessary since the argument is already a B*.
A* pa1 = const_cast<A*>(&ra1);
这是合法的,但映射成const是通常缺少类型才进行的,大部分的情况下,在哪儿移去指针的const或引用相关到类型成员与关键字的掩盖是合法的,在GotW #6有更多的关于const的正确用法
A* pa2 = const_cast<A*>(&ra2);
错误:如果指针在对象里使用写将产生不确定的行为,原因a2实际上上是const对象,为什么呢?,思考编译器是允许a2创建同样的const对象与使用它的信息存储在只读内存中并作最佳化,
注释:我没有给出使用const_cast转换成非const指向const指针的示例,理由是这是多余的,它早已是合法的分配非const指针指向const指针,我们只需要const_cast 去做相反的事。
B* pb3 = dynamic_cast<B*>(&c1);
错误(如果你尝试使用pb3):因为c1不是A B(原因:C不是起源于B,在实际上它也不完全起源于B),它将pb3设为NULL,唯一正确的类型映射是使用reinterpret_cast,而且使用它始终是不幸的。
A* pa3 = dynamic_cast<A*>(&b1);
错误:因为b1不是A A(原因:B不是起源于A,但它起源是虚基类A(也就是说从虚基类派生的都不能转换成该基类的类型)),这是非法的。
B* pb4 = static_cast<B*>(&d1);
这儿有细微的地方,因为源于基类的指针转换是不需要明确声明所以不需要。
D* pd = static_cast<D*>(pb4);
这儿有细微的地方,如果你预期这儿需要dynamic_cast的话是你会吃惊的。理由是当目标是已知道的时候,向下映射是静态的,但要小心:你是说编译器在事实上知道你的,它为什么开始指向实际上的它的类型,如果你错了,那么类型映射就不能通知你有问题(像dynamic_cast一样,如果类型映射失败它将返回空指针),且最多你能取得非真实的运行时错误与程序崩溃。
pa1 = dynamic_cast<A*>(pb2); pa1 = dynamic_cast<A*>(pb4);
这儿有两个看起来很相似,它们都尝试使用dynamic_cast把B*转换成A*,然而,第一个是错误的而第二个是对的。
这儿有个理由:同上面的注释,你不能使用dynamic_cast去映射指向实际上是B对象(pb2是指向b1对象)成A对象,因为B是从A进行私有继承,非公有继承,然而,第二个映射成功是因为pb4指向对象d1,且d像A一样是间接派生于基类(通过C),与dynamic_cast是允许映射越过继承体系,使用这种路径B* -> D* -> C* -> A*.
C* pc1 = dynamic_cast<C*>(pb4);
这儿有两个很细微,与前面的理由一样:dynamic_cast通过继承体系进行映射,所以这是合法且执行成功。
C& rc1 = dynamic_cast<C&>(*pb2); }
最后,这而也有细微的地方...,因为*pb2不是实际上上的a C,dynamic_cast将执出bad_cast异常失败信号。为什么呢?如果映射失败,dynamic_cast将返null,但这儿引用的对象是空的,如果引用失败那它不能返回空的引用,这儿除了抛出异常外没有失败的信号发送到客户端,所以那儿为什么只有bad_cast异常。
一些个人看法:
1.cber:dynamic_cast用于继承体系结构的转型(向下是自动的,向上需要验证),较为安全(不过需要是polymorphic classes)static_cast有点类似于C中的强制转型。具体的可以看看MEC Item2
2.dragonznz:One for compile time, one for runtime. I think.