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. Andoid多语言国际化策略

    目前手上的项目,为了普及覆盖更多的用户群,也已经开始实现了多语言设置这样的功能,不过今天我要说的不是微信,而是我们自己项目中的实现策略. 直接附上关键代码: package com.huolonglu ...

  2. 查看组成一个Index的column有哪些

    下面是创建一个表,并在上面建立一些index的SQL.我们会新建一个用户,然后再那个schema下运行下面的SQL. create table indtest (f1_num number(10)  ...

  3. local_listener参数的作用!

    转自:http://warehouse.itpub.net/post/777/472788 pmon只会动态注册port等于1521的监听,否则pmon不能动态注册listener,要想让pmon动态 ...

  4. C++ 随机数字以及随机数字加字母生成

    #include <time.h>#include <sys/timeb.h>void MainWindow::slot_clicked(){ QString strRand; ...

  5. 12 tensorflow实战:修改三维tensor矩阵的某个剖面

    # -*- coding: utf-8 -*- """ Created on Mon Apr 22 21:02:02 2019 @author: a "&quo ...

  6. 短时傅里叶变换(Short Time Fourier Transform)原理及 Python 实现

    原理 短时傅里叶变换(Short Time Fourier Transform, STFT) 是一个用于语音信号处理的通用工具.它定义了一个非常有用的时间和频率分布类, 其指定了任意信号随时间和频率变 ...

  7. LeetCode题解之Unique Morse Code Words

    1.题目描述 2.题目分析 将words 中的每一个string  直接翻译成对应的Morse 码,然后将其放入 set 中,最后返回set的大小即可,此处利用的set 中元素不重复的性质. 3.代码 ...

  8. Linux HugePages 配置与 Oracle 性能关系说明

    一.  HugePages 说明 1.1 HugePages 介绍 HugePages is a feature integrated into the Linux kernel with relea ...

  9. 常用CSS技术收藏

    常用CSS技术收藏 必须要掌握的技术 盒子模型 定位模型 定位模型 css sprite(雪碧/css精灵)相关 css sprite 坐标定位为何为负以及定位方法 布局 圣杯布局小结 规范 BEM ...

  10. javascript unshift()和shift()

    <html> <head> <meta http-equiv="Content-Type" content="text/html; char ...