19.2 C++(www.cppentry.com)中的强制
当隐式转换无法胜任时,我们可能就会发现需要显式地指示编译器到底进行何种转换(当然,编译器并不一定听你的话)。有时候,你只需要提供一个中间类型的占位符变量,如程序清单19.1所示的那样:
程序清单19.1
- class A
- {};
-
- class B
- : public A
- {};
-
- class C
- : public A
- {};
-
- class D
- : public B
- , public C
- {};
-
- D *d = new D();
- A *a = d; // 错误,二义性转换
- B *b = d; // 第一步 (D* => B*),没问题
- A *a2 = b; // 第二步 (B* => A*),没问题
另一种途径是使用强制(cast)。在C中你只能够使用C风格的强制,即使最保守地说,这也是一种"暴力"机制: - A *a = (B*)d; // 嗯……
即使一段时间以后我们改变了D的定义,令它不再继承自B,以上的代码仍然能够通过编译。造成的结果就是核心转储(Core dump)!
C++(www.cppentry.com)提供了4个强制操作符[Stro1997],用户被鼓励使用这4个强制来取代C风格强制。它们分别是static_cast、const_cast、dynamic_cast和reinterpret_cast,分别代表传统的C风格强制所提供的4种不同的功能,并且C++(www.cppentry.com)对它们的使用方式故意作了严格的限制[Stro1994]。 使用这4个强制,刚才的代码可以写成这样:
- A *a = static_cast<B*>(d); // 这下好多了
这么一来,对于原来的继承体系,这行代码就能通过编译,并可将D*转换为A*。倘若B和D之间不存在继承关系, 该行代码就无法通过编译,从而避免错误偷偷溜进运行期。以上的代码展示了强制的语法,即"强制名<目标类型>(操作数)(cast-name<target-type>(operand))"。该语法非常清晰,并且其形式上的一致性也是相当有帮助的:如果强制成功的话,就会返回"将操作数(operand)作为目标类型(target-type)来看待"的结果。随着本节的逐步展开,有一点你要记住:如果dynamic_cast的目标类型是引用并且转换失败,它就会抛出一个异常(即std::bad_cast)。
关于C++(www.cppentry.com)强制操作符当然还有许许多多的特性和重要的方面,你可以在大多数C++(www.cppentry.com)好书中读到它们[Dewh2003, Meye1996, Meye1998, Sutt2000, Stro1997]。目前我们还是着眼于在高质量代码中应该尽量使用C++(www.cppentry.com)强制这一事实,然后继续前进,并时不时地重提它们。