分享
 
 
 

Guru of the Week 条款17:转型

王朝other·作者佚名  2006-01-08
窄屏简体版  字體: |||超大  

GotW #17 Casts

著者:Herb Sutter

翻译:K ][ N G of @rk™

[声明]:本文内容取自www.gotw.ca网站上的Guru of the Week栏目,其著作权归原著者本人所有。译者kingofark在未经原著者本人同意的情况下翻译本文。本翻译内容仅供自学和参考用,请所有阅读过本文的人不要擅自转载、传播本翻译内容;下载本翻译内容的人请在阅读浏览后,立即删除其备份。译者kingofark对违反上述两条原则的人不负任何责任。特此声明。

Revision 1.0

Guru of the Week 条款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);

}

[解答]

1. 下列哪一种新风格的转型不能与C中的转型相对应?

只有dynamic_cast不能与C的转型相对应。其它的新风格转型都能与C中的旧风格转型相对应。

2. 对于下列每一个C中的转型语句,写出相应的新风格转型语句。其中哪一个语句如果不以新风格编写的话就是不正确的?

void f() {

A* pa; B* pb; C* pc;

pa = (A*)&ra1;

应该使用const_cast:const_cast<A*>(&ra1);

pa = (A*)&a2;

这一句无法以新风格的转型表达。最接近的方案是使用const_cast,但a2是一个const object,语句执行的结果是未定义的。

pb = (B*)&c1;

应该使用reinterpret_cast:pb=reinterpret_cast<B*>(&c1);

pc = (C*)&d1;

}

这一转型在C中是错误的。而在C++中,并不需要转型:pc=&d1;

3. 评判下列每一条C++转型语句的编写风格和正确性。

首先要注意:我们并不知道本条款中给出的类是否拥有虚拟函数;如果涉及转型的那些类并不拥有虚拟函数,那么下述所有对dynamic_cast的使用都是错误的。在下面的讨论中,我们假设所有的类都拥有虚拟函数,从而使所有的dynamic_cast用法都合法。

void g() {

unsigned char* puc = static_cast<unsigned char*>(&c);

signed char* psc = static_cast<signed char*>(&c);

错误:对两条语句我们都必须使用reinterpret_cast。这一开始或许会使你感到吃惊;这样做的原因是,char、signed char以及unsigned char是三个互不相同、区别开来的型别。尽管它们之间存在着隐式转换,它们也是互无联系的,因而指向它们的指针也是互无联系的。

void* pv = static_cast<void*>(&b1);

B* pb1 = static_cast<B*>(pv);

这两句都不错,但第一句中的转型是不必要的,因为本来就有从一个对象指针到void*的隐式转型动作存在。

B* pb2 = static_cast<B*>(&b1);

这一句不错,但其转型也是不必要的,因为其引数(argument)已经是一个B*。

A* pa1 = const_cast<A*>(&ra1);

这一句是合法的,但是使用转型来去掉const-ness(常量性)是潜在的不良风格的体现。在大部分情况下,即当你因合理的缘由而想要去掉指针或引用的const-ness(常量性)时,这都涉及到某些类成员,并通常会使用mutable关键字来完成。请参看GotW#6了解更多关于const-correctness的讨论。

A* pa2 = const_cast<A*>(&ra2);

错误:如果该指针被用来对对象施行写操作,那么就会产生未定义行为;因为a2是一个const object。要明白其原因,可以试想如果一个编译器了解到“a2是作为const object而被创建的”这个情况,并出于优化的考虑而将其存放在只读存储区,会发生什么事情。很明显,想通过转型而去掉这样一个对象的const属性是危险的。

注意:我并没有举例显示如何使用const_cast把一个non-const指针转型为一个const指针。因为这样做是多此一举;将一个non-const指针赋值给一个const指针,这本来就是合法的。我们只需要使用const_cast做相反的操作。

B* pb3 = dynamic_cast<B*>(&c1);

错误(当你企图使用pb3时发生):这一句会将pb3设置为null,因为c1不是一个(IS-NOT-A)B(因为C不是以public方式派生自B的,且实际上压根儿就不是派生自B的)。这里唯一合法可用的转型就是reinterpret_cast,但使用它也几乎总是很龌龊的。

A* pa3 = dynamic_cast<A*>(&b1);

错误:这一句是非法的,因为b1不是一个(IS-NOT-A)A(因为B不是以public方式派生自A的,而是以private方式)。

B* pb4 = static_cast<B*>(&d1);

这一句不错,但也没必要做转型,因为derived-to-base(由派生类到基类)的指针转换可以被隐式的完成。

D* pd = static_cast<D*>(pb4);

这一句不错。如果你原先认为这里需要的是dynamic_cast的话,这或许会使你感到吃惊。其原因是,当目标已知的时候,向下转型(downcast)可以是静态的,此时要注意:你这样等于是在告诉编译器,你知道“被指针所指的正是那种型别”这个事实。如果你错了,那么这个转型将无法告知你已经出现的问题(dynamic_cast在转型失败时就能返回一个null pointer以告知你出现了问题),于是你此时至多也只能得到各种不同的运行期错误以及/或者程序崩溃。

pa1 = dynamic_cast<A*>(pb2);

pa1 = dynamic_cast<A*>(pb4);

这两句看起来很相似。两句都试图使用dynamic_cast来把B*转换为A*。然而,第一个是错误的而第二个是正确的。

原因是:正如前面所述,你不能使用dynamic_cast把一个指向B对象(这里pb2指向对象b1)的指针转换为指向A对象的指针,因为B是以private方式从A进行继承的,不是以public方式。然而第二句中的转型是成功的,这是因为pb4指向对象d1,而D(通过C)将A作为一个间接的public base class,从而让dynamic_cast可以沿着B*-->D*-->C*-->A*的路径在继承层次结构中进行转型。

C* pc1 = dynamic_cast<C*>(pb4);

这一句也不错,其原因与上面的一样:dynamic_cast可以穿越继承层次进行交叉转型(cross-cast),因此这一句是合法的并可以成功执行。

C& rc1 = dynamic_cast<C&>(*pb2);

最后这一句是错的……因为*pb2并不真的就是一个C,dynamic_cast会抛出一个bad_cast异常来报告失败。为什么?因为dynamic_cast可以在指针转型(pointer cast)失败时返回null,但由于没有null reference一说,因此当一个引用转型(reference cast)失败时便无法返回null reference。除了抛出一个异常以外,没有别的方法来报告错误了——标准的bad_cast异常类也就是因此而来的。

(完)

 
 
 
免责声明:本文为网络用户发布,其观点仅代表作者个人观点,与本站无关,本站仅提供信息存储服务。文中陈述内容未经本站证实,其真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。
2023年上半年GDP全球前十五强
 百态   2023-10-24
美众议院议长启动对拜登的弹劾调查
 百态   2023-09-13
上海、济南、武汉等多地出现不明坠落物
 探索   2023-09-06
印度或要将国名改为“巴拉特”
 百态   2023-09-06
男子为女友送行,买票不登机被捕
 百态   2023-08-20
手机地震预警功能怎么开?
 干货   2023-08-06
女子4年卖2套房花700多万做美容:不但没变美脸,面部还出现变形
 百态   2023-08-04
住户一楼被水淹 还冲来8头猪
 百态   2023-07-31
女子体内爬出大量瓜子状活虫
 百态   2023-07-25
地球连续35年收到神秘规律性信号,网友:不要回答!
 探索   2023-07-21
全球镓价格本周大涨27%
 探索   2023-07-09
钱都流向了那些不缺钱的人,苦都留给了能吃苦的人
 探索   2023-07-02
倩女手游刀客魅者强控制(强混乱强眩晕强睡眠)和对应控制抗性的关系
 百态   2020-08-20
美国5月9日最新疫情:美国确诊人数突破131万
 百态   2020-05-09
荷兰政府宣布将集体辞职
 干货   2020-04-30
倩女幽魂手游师徒任务情义春秋猜成语答案逍遥观:鹏程万里
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案神机营:射石饮羽
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案昆仑山:拔刀相助
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案天工阁:鬼斧神工
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案丝路古道:单枪匹马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:与虎谋皮
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:李代桃僵
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:指鹿为马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:小鸟依人
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:千金买邻
 干货   2019-11-12
 
推荐阅读
 
 
 
>>返回首頁<<
 
靜靜地坐在廢墟上,四周的荒凉一望無際,忽然覺得,淒涼也很美
© 2005- 王朝網路 版權所有