[C++基础]强制转换运算符dynamic_cast

强制转换运算符是一种特殊的运算符,它把一种数据类型转换为另一种数据类型。强制转换运算符是一元运算符,它的优先级与其他一元运算符相同。

语法: dynamic_cast < new-type> ( expression )
该运算符把expression转换成new-type类型的对象。
new-type必须是类的指针、类的引用或者void *;
如果new-type是类指针类型,那么expression也必须是一个指针,
如果new-type是一个引用,那么expression也必须是一个引用。

若转型成功,则 dynamic_cast 返回 new_type 类型的值。
若转型失败且 new_type 是指针类型,则它返回该类型的空指针。
若转型失败且 new_type 是引用类型,则它抛出匹配类型 std::bad_cast 处理块的异常。

class CBase
{
}; //基类
class CDerived : public CBase
{
}; //继承类
CDerived dc;
CDerived *dp = &dc;
//使用dynamic_cast将指向继承类的指针转化为指向基类的指针
CBase *bp = dynamic_cast<CBase *>(dp);

//使用dynamic_cast将指向继承类的引用转化为指向基类的引用
CBase &br = dynamic_cast<CBase &>(dc);

dynamic_cast主要用于类层次间的上行转换和下行转换,还可以用于类之间的交叉转换。
在类层次间进行上行转换(Up Cast,子类向基类的向上转型)时,dynamic_cast和static_cast的效果是一样的;
在进行下行转换(Down Cast,基类向子类的向下转型)时,dynamic_cast具有类型检查的功能,比static_cast更安全。

作为四个内部类型转换操作符之一的dynamic_cast和传统的C风格的强制类型转换有着巨大的差别。除了dynamic_cast以外的转换,其行为的都是在编译期就得以确定的,转换是否成功,并不依赖被转换的对象。而dynamic_cast则不然。dynamic_cast依赖于RTTI信息,在转换时,dynamic_cast会检查转换的source对象是否真的可以转换成target类型,这种检查不是语法上的,而是真实情况的检查。通常,RTTI相关部分,许多编译器都是通过vtable找到对象的RTTI信息的。

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

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

#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 0;
}

/********************* 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)来判定的,它还受到编译器的影响,有些编译器需要设置开启才能让程序正确运行。

总结

dynamic_cast < new-type> ( expression ):

  • 1.expression对象必须有虚函数指针,否则编译失败。
  • 2.从子类到基类的指针转换,转型成功,运行不会闪退,完全正确
  • 3.从基类到子类的转换,返回空指针
  • 4.没有关系的两个类之间的转换,返回空指针

static_cast <new-type> ( expression ):

  • 1.expression类与new-type类必须存在继承关系,否则编译失败。
  • 2.从子类到基类的指针转换,转型成功,运行不会闪退,完全正确
  • 3.从基类到子类的转换,转型成功,调用子类函数可能闪退,(若子类函数中使用了不存在的成员数据时,就会闪退,否则就不会闪退,那是因为函数存储在代码区域内存中;且调用的是虚函数,则会执行多态)
  • 4.没有关系的两个类之间的转换,否则编译失败
发布了449 篇原创文章 · 获赞 180 · 访问量 87万+

猜你喜欢

转载自blog.csdn.net/ouyangshima/article/details/88915175
今日推荐