dynamic_cast 是 RTTI (Runtime Type Identification,运行阶段类型识别)组件之一。如果可能的话,它将使用一个指向基类的指针来生成一个指向派生类的指针;否则,该运算符返回 0 --- 空指针。
注意: RTTI 只适用于包含虚函数的类。
dynamic_cast 它不能告诉你“指针指向的是哪类对象”,但可以回答“是否可以安全的将对象的地址赋给特定类型的指针”这样的问题。事实上,与“指针指向的是哪种类型的对象” 相比,问题“类型转换是否安全”更为通用,也更有用。通常想知道类型的原因在于:对于类对象,知道了类型后,就可以知道调用特定的方法是否安全。实际上,要调用方法,类型并不一定要完全匹配;如定义了虚函数的基类,基类的指针也可以指向派生类的对象,而该指针也可以调用派生类里被重新定义了的虚方法。
先看一下 dynamic_cast 的语法:- Superb *pm = dynamic_cast<Superb *> (pg);
复制代码 其中,pg 指向一个对象。这提出一个问题:指针 pg 的类型是否可被安全地转换为 Superb * ?如果可以,运算符将返回对象的地址,否则返回一个空指针。
注意:通常,如果指向的对象 (*pt) 的类型为 Type 或者是从 Type 直接或间接派生而来的类型,则下面的表达式将指针 pt 转换为 Type 类型的指针。- 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 对象的引用:- #include <typeinfo> // for bad_cast
- ...
- try {
- Superb &rs = dynamic_cast<Superb &> (rg);
- ...
- }
- catch (bad_cast &) {
- ...
- };
复制代码 |