C++中的类型转换与类型识别 2

来源:互联网 发布:amx105突击型数据 编辑:程序博客网 时间:2024/06/03 10:30

C++ 基础内容, 不值一提

AuthorJacky 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++中的RTTIrun-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机制,看起来会让人感到迷惑,同时有些复杂,如果弄清楚这两个重要的内在机制在理解类型转换就会很容易。

原创粉丝点击