C++ 基础内容, 不值一提
Author:Jacky Wu 2006-4-17
引用该文章,须注明其出处 http://blog.csdn.net/imwkj
3:多态中的类型转换 |
多态中的类型转换是通过使用基类的指针或者引用来实现的,包括“向上类型转换(upcasting)”和“向下类型转换(downcasting)”,其中向下类型转换的实现是通过virtual 机制实现的,关于C++ 中virtual机制要好好理解,这里仅仅聊聊它们之中的类型转换。 a.向上类型转换 upcasting //Inheritance & upcasting enum note { middleC, Csharp, Eflat }; class Instrument { public: void play(note) const { cout << "Instrument::play" << endl; } };
class Wind : public Instrument { public: void play(note) const { cout << "Wind::play" << endl; } };
void tune(Instrument& i) { //这里可能发生类型转换 i.play(middleC); } int main() { Wind flute; tune(flute); //传递的是Wind型对象,发生向上类型转换 } //输出结果: Instrument::play 在这里由于自动发生了类型转换,Wind 类型被自动转换到Instrument基类型上(只有显示继承关系才能发生这种转换),在这种情况下,显然程序输出的结果不是我们所期望的,如果想发生正确的调用就必须使用虚机制(virtual class& function)。在这里,我们只谈类型转换。我们先来看一下发生正确调用的类型转换:
//Inheritance & upcasting enum note { middleC, Csharp, Eflat }; class Instrument { public: virtual void play(note) const { //只是在这里声明为虚函数 cout << "Instrument::play" << endl; } };
class Wind : public Instrument { public: void play(note) const { cout << "Wind::play" << endl; } };
void tune(Instrument& i) { //这里可能发生类型转换 i.play(middleC); } int main() { Wind flute; tune(flute); //传递的是Wind型对象,发生向上类型转换 } 输出结果: Wind::play 这才是正确的“向上类型转换”,关于类的虚机制,我们在后面专门介绍。
b.向下类型转换(downcasting) 由于向上类型转换是安全的转换,在转换过程中会变成更一般的基类型,所以在转换后,一般不会出现错误。但向下类型转换就会出现迷惑,由一般基类型转换到子类型就会出现不清楚的问题。举个例子,Pet (宠物) 代表一个类型,Cat 和 Dog是它的派生子类,从Cat,或者Dog转换到Pet,是可以理解并正确的,但是如果是一个Pet,那如何知道它是必须转换成Cat还是Dog??这必须使用C++中的RTTI(run-time type identification) 运行时刻类型识别机制来进行处理。 有几个关键字要用到,他们是typeid , static_cast , dynamic_cast,为了能够安全的进行 downcasting,我们就得学会使用这些关键字,但是更好的方法是使用C++提供的虚机制。 由于C++在考虑到了效率因素,它在类的实现中并没有给每个类额外的加上“类型信息” (class information) ,所以我们就不能直接对普通类进行转换,因此,在使用这个功能的时候,类必须是多态方式实现的(必须有virtual机制),看代码: class Pet { public: virtual ~Pet() {} };
class Dog : public Pet {}; class Cat: public Pet {}; int main() { Pet* pt = new Cat; //Upcast //尝试 将 pt转换为 Dog,这中转换是错误的,dg = 0 Dog* dg = dynamic_cast<Dog*> (pt); //尝试转换为Cat,正确 Cat* ct = dynamic_cast<Cat*> (pt); cout << " dg = " << dg << endl; cout << " ct = " << ct << endl; } 输出结果: dg = 0 ct = 0x3d2480 可见,如果发生成功的dynamic_cast转换,返回得到的对象的地址,相反,失败的转换返回0,在这种情况下,我们必须对返回的结果做出判定,否则就可能会发生错误。 这样,程序会花费额外的开销,如果我们知道被转换的对象是何种类型,我们就可以使用static_cast,同时使用typeid来确保这种转换是正确的,看下面的代码: #include <iostream> #include <typeinfo> using namespace std; class Pet { public: virtual ~Pet() {} };
class Dog : public Pet {}; class Cat: public Pet {}; int main() { Dog adog; Pet* apet = &adog; //正确的向上类型转换
Dog* dg = 0; Cat* ct = 0; cout << typeid(apet).name()<< endl; cout << typeid(dg).name() << endl; cout << typeid(ct).name() << endl; //上面的输出结果和编译器相关 if(typeid(apet) == typeid(dg)) {//利用typeid()确定其所属类型 dg = static_cast<Dog*>(apet); }
if(typeid(apet) == typeid(ct)) { ct = static_cast<Cat*>(apet); }
if(dg != 0) { cout << "It is a Dog/n"; } if(ct != 0) { cout << "It is a Cat/n"; } } 输出结果: P3Pet P3Dog P3Cat
在这个程序中用了头文件<typeinfo> ,这是C++为RTTI提供支持的一个头文件。用typeid操作符来确定对象指针的所属类型,它返回的是一个指向静态type_info型对象的引用。由于static_cast , dynamic_cast不能转换到其他外部类型,所以这种转换是安全的,但是有一定的效率损失,在一般情况下我们使用dynamic_cast而不用static_cast。 C++中的类型转换由于牵涉到虚机制和RTTI机制,看起来会让人感到迷惑,同时有些复杂,如果弄清楚这两个重要的内在机制在理解类型转换就会很容易。 |