dynamic_cast <new_type> (expression)

dynamic_cast运算符,应该算是四个里面最特殊的一个,因为它涉及到编译器的属性设置,而且牵扯到的面向对象的多态性跟程序运行时的状态也有关系,所以不能完全的使用传统的转换方式来替代。但是也因此它是最常用,最不可缺少的一个运算符。

static_cast一样,dynamic_cast的转换也需要目标类型和源对象有一定的关系:继承关系。 更准确的说,dynamic_cast是用来检查两者是否有继承关系。因此该运算符实际上只接受基于类对象的指针和引用的类转换。从这个方面来看,似乎dynamic_cast又和reinterpret_cast是一致的,但实际上,它们还是存在着很大的差别。

还是用代码来解释,让编译器来说明吧。

/////////////////////////////////////////////////////////////////////////////
// cast_operator_comparison.cpp
// Language: C++
// Complier: Visual Studio 2010, Xcode3.2.6
// Platform: MacBook Pro 2010
// Application: none
// Author: Ider, Syracuse University ider.cs@gmail.com
///////////////////////////////////////////////////////////////////////////
#include <string>
#include <iostream>
using namespace std; class Parents
{
public:
Parents(string n="Parent"){ name = n;}
virtual ~Parents(){} virtual void Speak()
{
cout << "\tI am " << name << ", I love my children." << endl;
}
void Work()
{
cout << "\tI am " << name <<", I need to work for my family." << endl;;
}
protected:
string name;
}; class Children : public Parents
{
public:
Children(string n="Child"):Parents(n){ } virtual ~Children(){} virtual void Speak()
{
cout << "\tI am " << name << ", I love my parents." << endl;
}
/*
**Children inherit Work() method from parents,
**it could be treated like part-time job.
*/
void Study()
{
cout << "\tI am " << name << ", I need to study for future." << endl;;
} private:
//string name; //Inherit "name" member from Parents
}; class Stranger
{
public:
Stranger(string n="stranger"){name = n;}
virtual ~Stranger(){} void Self_Introduce()
{
cout << "\tI am a stranger" << endl;
}
void Speak()
{
//cout << "I am a stranger" << endl;
cout << "\tDo not talk to "<< name << ", who is a stranger." << endl;
}
private:
string name;
}; int main() { /******* cast from child class to base class *******/
cout << "dynamic_cast from child class to base class:" << endl;
Children * daughter_d = new Children("Daughter who pretend to be my mother");
Parents * mother_d = dynamic_cast<Parents*> (daughter_d); //right, cast with polymorphism
mother_d->Speak();
mother_d->Work();
//mother_d->Study(); //Error, no such method cout << "static_cast from child class to base class:" << endl;
Children * son_s = new Children("Son who pretend to be my father");
Parents * father_s = static_cast<Parents*> (son_s); //right, cast with polymorphism
father_s->Speak();
father_s->Work();
//father_s->Study(); //Error, no such method cout << endl; /******* cast from base class to child class *******/
cout << "dynamic_cast from base class to child class:" << endl;
Parents * father_d = new Parents("Father who pretend to be a my son");
Children * son_d = dynamic_cast<Children*> (father_d); //no error, but not safe
if (son_d)
{
son_d->Speak();
son_d->Study();
}
else cout << "\t[null]" << endl; cout << "static_cast from base class to child class:" << endl;
Parents * mother_s = new Parents("Mother who pretend to be a my daugher");
Children * daughter_s = static_cast<Children*> (mother_s); //no error, but not safe
if (daughter_s)
{
daughter_s->Speak();
daughter_s->Study();
}
else cout << "\t[null]" << endl; cout << endl; /******* cast between non-related class *******/
cout << "dynamic_cast to non-related class:" << endl;
Stranger* stranger_d = dynamic_cast<Stranger*> (daughter_d);
if (stranger_d)
{
stranger_d->Self_Introduce();
stranger_d->Speak();
}
else cout <<"\t[null]"<<endl; //Stranger* stranger_s = static_cast<Stranger*> (son_s); //Error, invalid cast cout << "reinterpret_cast to non-related class:" << endl;
Stranger* stranger_r = reinterpret_cast<Stranger*> (son_s);
if (stranger_r)
{
stranger_d->Self_Introduce();
//stranger_d->Speak(); //This line would cause program crush,
//as "name" could not be found corretly.
}
else cout << "\t[null]" << endl; cout << endl; /******* cast back*******/
cout << "use dynamic_cast to cast back from static_cast:" << endl;
Children* child_s = dynamic_cast<Children*> (father_s);
if (child_s)
{
child_s->Speak();
child_s->Work();
}
else cout << "\t[null]" << endl; //cout<<typeid(stranger_r).name()<<endl; cout << "use dynamic_cast to cast back from reinterpret_cast:" << endl;
Children* child_r = dynamic_cast<Children*> (stranger_r);
if (child_r)
{
child_r->Speak();
child_r->Work();
}
else cout << "\t[null]" << endl; delete daughter_d;
delete son_s;
delete father_d;
delete mother_s; return ;
} /********************* Result *********************/ //dynamic_cast from child class to base class:
// I am Daughter who pretend to be my mother, I love my parents.
// I am Daughter who pretend to be my mother, I need to work for my family.
//static_cast from child class to base class:
// I am Son who pretend to be my father, I love my parents.
// I am Son who pretend to be my father, I need to work for my family.
//
//dynamic_cast from base class to child class:
// [null]
//static_cast from base class to child class:
// I am Mother who pretend to be a my daugher, I love my children.
// I am Mother who pretend to be a my daugher, I need to study for future.
//
//dynamic_cast to non-related class:
// [null]
//reinterpret_cast to non-related class:
// I am a stranger
//
//use dynamic_cast to cast back from static_cast:
// I am Son who pretend to be my father, I love my parents.
// I am Son who pretend to be my father, I need to work for my family.
//use dynamic_cast to cast back from reinterpret_cast:
// [null]

从上边的代码和输出结果可以看出:

对于从子类到基类的指针转换,static_cast和dynamic_cast都是成功并且正确的(所谓成功是说转换没有编译错误或者运行异常;所谓正确是指方法的调用和数据的访问输出是期望的结果),这是面向对象多态性的完美体现。

而从基类到子类的转换,static_cast和dynamic_cast 都是成功的,但是正确性方面,我对两者的结果都先进行了是否非空的判别:dynamic_cast的结果显示是空指针,而static_cast则是非空 指针。但很显然,static_cast的结果应该算是错误的,子类指针实际所指的是基类的对象,而基类对象并不具有子类的Study()方法(除非妈妈 又想去接受个"继续教育")。

对于没有关系的两个类之间的转换,输出结果表明,dynamic_cast依然是返回一个空指针以表示转换是不成立的;static_cast直接在编译期就拒绝了这种转换。

reinterpret_cast成功进行了转换,而且返回的值并不是空指针,但是结果显然是错误的,因为Children类显然不具有 Stranger的Self_Introduce()。虽然两者都具有name数据成员和Speak()方法,,Speak()方法也只是调用了该相同名 称的成员而已,但是对于Speak()的调用直接造成了程序的崩溃。

其实前面static_cast的转换的结果也会跟reinterpret_cast一样造成的程序的崩溃,只是类的方法都只有一份,只有数据成员属于对象, 所以在调用那些不会访问对象的数据的方法时(如Stranger的Self_Introduce())并不会造成崩溃。而 daughter_s->Speak();和daughter_s->Study();调用了数据成员却没有出现运行错误,则是因为该成员是 从基类继承下来的,通过地址偏移可以正确的到达数据成员所在的地址以读取出数据。

最后,程序里还用dynamic_cast希望把用其他转换运算符转换过去的指针转换回来。 对于使用static_cast转换后指向了子类对象的基类指针,dynamic_cast判定转换是合理有效的,因此转换成功获得一个非空的指针并且正 确输出了结果;而对于reinterpret_cast转换的类型,的确如它的功能一样——重新解析,变成新的类型,所以才得到dynamic_cast 判定该类型已经不是原来的类型结果,转换得到了一个空指针。

总得说来,static_cast和reinterpret_cast运算符要么直接被 编译器拒绝进行转换,要么就一定会得到相应的目标类型的值。 而dynamic_cast却会进行判别,确定源指针所指的内容,是否真的合适被目标指针接受。如果是否定的,那么dynamic_cast则会返回 null。这是通过检查"运行期类型信息"(Runtime type information,RTTI)来判定的,它还受到编译器的影响,有些编译器需要设置开启才能让程序正确运行(导师的PPT详细介绍了Visual Studio的情况),因此dynamic_cast也就不能用传统的转换方式来实现了。

虚函数(virtual function)对dynamic_cast的作用

已经在前面反复提到过面向对象的多态性,但是这个多态性到底要如何体现呢?dynamic_cast真的允许任意对象指针之间进行转换,只是最后返回个null值来告知转换无结果吗?

实际上,这一切都是虚函数(virtual function)在起作用。

在C++的面对对象思想中,虚函数起到了很关键的作用,当一个类中拥有至少一个虚函数,那么编译器就会构建出一个虚函数表(virtual method table)来指示这些函数的地址,假如继承该类的子类定义并实现了一个同名并具有同样函数签名(function siguature)的方法重写了基类中的方法,那么虚函数表会将该函数指向新的地址。此时多态性就体现出来了:当我们将基类的指针或引用指向子类的对象的时候,调用方法时,就会顺着虚函数表找到对应子类的方法而非基类的方法。

当然虚函数表的存在对于效率上会有一定的影响,首先构建虚函数表需要时间,根据虚函数表寻到到函数也需要时间。

因为这个原因如果没有继承的需要,一般不必在类中定义虚函数。但是对于继承来说,虚函数就变得很重要了,这不仅仅是实现多态性的一个重要标志,同时也是dynamic_cast转换能够进行的前提条件。

假如去掉上个例子中Stranger类析构函数前的virtual,那么语句

Children* child_r = dynamic_cast<Children*> (stranger_r);

在编译期就会直接报出错误,具体原因不是很清楚,我猜测可能是因为当类没有虚函数表的时候,dynamic_cast就不能用RTTI来确定类的具体类型,于是就直接不通过编译。

这不仅仅是没有继承关系的类之间的情况,如果基类或者子类没有任何虚函数(如果基类有虚函数表,子类当然是自动继承了该表),当他们作为dynamic_cast的源类型进行转换时,编译也会失败。

这种情况是有可能存在的,因为在设计的时候,我们可能不需要让子类重写任何基类的方法。但实际上,这是不合理的。导师在讲解多态性的时候,时刻强调了一点:如果要用继承,那么一定要让析构函数是虚函数;如果一个函数是虚函数,那么在子类中也要是虚函数。

我会将导师关于"为何继承中析构函数必须是虚函数"的讲解总结一下,当然你也可以看这边文章来了解原因。

C++标准转换运算符dynamic_cast的更多相关文章

  1. C++标准转换运算符

    C++类型转换在实际编程中会经常使用,其实,本质上对象的类型用来解释(interpret)对象.因为,每个对象都占据一块内存空间,这块内存空间存放了一段二进制数据.通过标记该对象的类型,告诉如何看待这 ...

  2. C++标准转换运算符reinterpret_cast

    C++标准转换运算符reinterpret_cast reinterpret_cast <new_type> (expression) reinterpret_cast运算符是用来处理无关 ...

  3. C++标准转换运算符const_cast

    前面讲了C++继承并扩展C语言的传统类型转换方式,最后留下了一些关于指针和引用上的转换问题,没有做详细地讲述.C++相比于C是一门面向对象的语言,面向对象最大的特点之一就是具有“多态性(Polymor ...

  4. C++标准转换运算符 --四种

    具体归纳如下: reinterpret_cast 函数将一个类型的指针转换为另一个类型的指针. 这种转换不用修改指针变量值存放格式(不改变指针变量值),只需在编译时重新解释指针的类型就可做到.rein ...

  5. 【转】C++标准转换运算符reinterpret_cast

    reinterpret_cast<new_type> (expression) reinterpret_cast运算符是用来处理无关类型之间的转换:它会产生一个新的值,这个值会有与原始参数 ...

  6. 【转】C++标准转换运算符static_cast

    static_cast<new_type> (expression) 虽然const_cast是用来去除变量的const限定,但是static_cast却不是用来去除变量的static引用 ...

  7. 【转】C++标准转换运算符const_cast

    const_cast转换符是用来移除变量的const或volatile限定符. 对于const变量,我们不能修改它的值,这是这个限定符最直接的表现.但是我们就是想违背它的限定希望修改其内容怎么办呢? ...

  8. C++标准转换运算符static_cast

    该运算符把expression转换为type-id类型,但没有运行时类型检查来保证转换的安全性.   中文名 暂无 外文名 static_cast 分    类 强制类型转换 类    型 C++ s ...

  9. const_cast标准转换运算符

    #include <iostream> using namespace std; class A { public: A() { a=; } public: int a; }; void ...

随机推荐

  1. 用jquery实现带左右按键的轮播图

    成品如下: 简单来说就是点击“右”按钮时,转换到右边的下一幅图片,同时上面的小方块颜色也跟着改变,如果已经是最后一幅图片,再点击“右”,则转换到第一幅图片,是直接向左移找到第一幅图的,明天再做一下无缝 ...

  2. 云数据库Redis版256M双机热备款

    云数据库Redis版是兼容Redis协议标准的.提供持久化的缓存式数据库服务,基于高可靠双机热备架构:全新推出的256M小规格款,适用于高QPS.小数据量业务,并支持免费全量迁移,完美服务于个人开发者 ...

  3. jsonp 返回以前必须要再转一次json

    public static void main(String[] args) {        String ajaxJsonStr = null;        AjaxJson ajaxJson ...

  4. java 接口默认修饰符

    概论: java接口可以是public 的,也可以是friendly的,但一定是abstracted的. java接口里的方法只能是public的.abstract的. java接口里的成员变量只能是 ...

  5. C#Owin auth20开发 OwinStartup 不会触发的解决办法

    在使用owin auth20设置token时候遇到一个问题.项目中已经存在如下初始化配置类 using Microsoft.Owin; using Owin; [assembly: OwinStart ...

  6. ORACLE查询表最近更改的数据 VERSIONS BETWEEN TIMESTAMP MINVALUE AND MAXVALUE

    转自 http://blog.chinaunix.net/uid-8504518-id-3325718.html 今天开发人员问我如何快速并方便的查出一张表最近被更改的记录,这个需求很简单,由于是查最 ...

  7. apache ftp server的简单入门(java应用内嵌ftp server)

    Apache Ftp Server:(强调) Apache Ftp Server 是100%纯Java的FTP服务器软件,它采用MINA网络框架开发具有非常好的性能.Apache FtpServer ...

  8. commonjs详解

    marked here a well written artical http://javascript.ruanyifeng.com/nodejs/module.html

  9. python及pandas,numpy等知识点技巧点学习笔记

    python和java,.net,php web平台交互最好使用web通信方式,不要使用Jypython,IronPython,这样的好处是能够保持程序模块化,解耦性好 python允许使用'''.. ...

  10. ORACLE RAC clusterware/GI 启动诊断流程图11.2+