曲径通幽论坛

 找回密码
 立即注册
搜索
查看: 3485|回复: 0
打印 上一主题 下一主题

[RTTI]dynamic_cast 运算符

[复制链接]

716

主题

734

帖子

2946

积分

超级版主

Rank: 9Rank: 9Rank: 9

积分
2946
跳转到指定楼层
楼主
发表于 2014-1-5 18:29:35 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
dynamic_cast 是 RTTI (Runtime Type Identification,运行阶段类型识别)组件之一。如果可能的话,它将使用一个指向基类的指针来生成一个指向派生类的指针;否则,该运算符返回 0 --- 空指针。
注意: RTTI 只适用于包含虚函数的类。

dynamic_cast 它不能告诉你“指针指向的是哪类对象”,但可以回答“是否可以安全的将对象的地址赋给特定类型的指针”这样的问题。事实上,与“指针指向的是哪种类型的对象” 相比,问题“类型转换是否安全”更为通用,也更有用。通常想知道类型的原因在于:对于类对象,知道了类型后,就可以知道调用特定的方法是否安全。实际上,要调用方法,类型并不一定要完全匹配;如定义了虚函数的基类,基类的指针也可以指向派生类的对象,而该指针也可以调用派生类里被重新定义了的虚方法。

先看一下 dynamic_cast 的语法:
  1. Superb *pm = dynamic_cast<Superb *> (pg);
复制代码
其中,pg 指向一个对象。这提出一个问题:指针 pg 的类型是否可被安全地转换为 Superb * ?如果可以,运算符将返回对象的地址,否则返回一个空指针。

注意:通常,如果指向的对象 (*pt) 的类型为 Type 或者是从 Type 直接或间接派生而来的类型,则下面的表达式将指针 pt 转换为 Type 类型的指针。
  1. dynamic_cast <Type *> (pt)
复制代码
否则,结果为 0,即空指针。

考虑下面的代码:
[C++] 纯文本查看 复制代码
// rtti1.cpp -- using the RTTI dynamic_cast operator
#include <iostream>
#include <cstdlib>
#include <ctime>

using std::cout;

class Grand
{
private:
    int hold;
public:
    Grand(int h = 0) : hold(h) {}
    virtual void Speak() const { cout << "I am a grand class!\n";}
    virtual int Value() const { return hold; }
};

class Superb : public Grand
{
public:
    Superb(int h = 0) : Grand(h) {}
    void Speak() const {cout << "I am a superb class!!\n"; }
    virtual void Say() const
        { cout << "I hold the superb value of " << Value() << "!\n";}
};

class Magnificent : public Superb
{
private:
    char ch;
public:
    Magnificent(int h = 0, char c = 'A') : Superb(h), ch(c) {}
    void Speak() const {cout << "I am a magnificent class!!!\n";}
    void Say() const {cout << "I hold the character " << ch <<
               " and the integer "  << Value() << "!\n"; }
};

Grand * GetOne();

int main()
{
    std::srand(std::time(0));
    Grand * pg;
    Superb * ps;
    for (int i = 0; i < 5; i++)
    {
        pg = GetOne();
        pg->Speak();
        if( ps = dynamic_cast<Superb *>(pg))
            ps->Say();
    }
    // std::cin.get();
    return 0;
}

Grand * GetOne()    // generate one of three kinds of objects randomly
{
    Grand * p;
    switch( std::rand() % 3)
    {
        case 0: p = new Grand(std::rand() % 100);
                    break;
        case 1: p = new Superb(std::rand() % 100);
                    break;
        case 2: p = new Magnificent(std::rand() % 100, 
                              'A' + std::rand() % 26);
                    break;
    }
    return p; 
}

在上面的程序中,定义了 3 个类:Grand,Superb 和 Magnificent 。

Grand 中定义了一个虚函数 Speak(),其它两个类重新定义了该虚函数。

Superb 中定义了一个虚函数 Say(),在 Manificent 中也重新定义了该函数。

main 中定义了 GetOne() 函数,该函数随机创建上述 3 种类中的某个对象,并对其初始化,然后将 Grand * 指针返回到 pg 上。接着,使用 pg 调用 Speak() 函数;由于该函数是个虚函数,因此代码能够正确地调用指向该对象的 Speak() 版本;也就是说,虽然 pg 是基类指针,但由于 speak() 是基类中定义的虚函数,而现在基类指针指向了派生类对象,那么调用相应的虚函数时,用的是派生类中的版本。

然而,不能用相同的方式,即使用指向 Grand 的指针来调用 Say() 函数,因为在 Grand 类中没有定义它。此时,就可以使用 dynamic_cast 来检查是否可以将 pg 的类型安全地转化为 Superb 指针了。如果对象的类型为 Superb 或 Magnificent ,则可以安全转换。在这两种情况下,都可以安全地调用 Say() 函数。

dynamic_cast 也可以用于引用,但用法稍微有点不同:由于没有与空指针对应的引用值(空指针已经为 0),因此无法使用特殊的引用值来指示转换失败。当请求转换不正确时,dynamic_cast 将引发类型为 bad_cast 异常,这种异常是从 exception 类派生而来的,它在头文件 typeinfo 中定义。因此,可以像下面这样使用该运算符,其中 rg 是对 Grand 对象的引用:
  1. #include <typeinfo> // for bad_cast
  2. ...
  3. try {
  4.    Superb &rs = dynamic_cast<Superb &> (rg);
  5.    ...
  6. }
  7. catch (bad_cast &) {
  8.    ...
  9. };
复制代码
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|手机版|Archiver|曲径通幽 ( 琼ICP备11001422号-1|公安备案:46900502000207 )

GMT+8, 2024-4-30 10:24 , Processed in 0.075386 second(s), 23 queries .

Powered by Discuz! X3.2

© 2001-2013 Comsenz Inc.

快速回复 返回顶部 返回列表