现在的位置: 首页 > 综合 > 正文

C++_转换运算符_dynamic_cast

2013年10月10日 ⁄ 综合 ⁄ 共 6182字 ⁄ 字号 评论关闭

dynamic_cast在什么时候使用:

使用时间:子类和父类之间的多态类型转换

引入dynamic_cast的原因:

举例说明:

#include <iostream>
using namespace std;

class Base
{
public:
	virtual void BaseAAA()
	{
		cout<<"BaseAAA"<<endl;
	}

	virtual void AAA()
	{
		cout<<"Base::VirtualAAA"<<endl;
	}
};

class Derived : public Base
{
public:
	void DerivedAAA()
	{
		cout<<"DerivedAAA"<<endl;
	}

	void AAA()
	{
		cout<<"Derived::VirtualAAA"<<endl;
	}
};


int main()
{
	//为了实现虚函数的调用,让基类指针指向了派生类对象。
	Base* pBaseOne = new Derived;

	//使用基类指针pBaseOne,可以访问虚函数,实现多态
	pBaseOne->AAA();  //输出Derived::VirtualAAA
	
	//但是问题来了,让基类指针指向派生类后,基类指针无法访问派生类的成员了。
	//pBaseOne->DerivedAAA();//操作失败

	//这时需要对这个基类指针进行强制类型转换,获得派生类对象,之后操作派生类自己的成员
	Derived* pDerived = dynamic_cast<Derived*>(pBaseOne);
	pDerived->DerivedAAA(); //输出DerivedAAA

	system("pause");
	return 1;
}

具体来说:

为了实现多态,让基类指针指向了派生类对象,但是这个基类指针无法操作派生类自己的成员了,所以就想把这个指针由基类指针转换为派生类指针,此时就可以使用dynamic_cast。即,已经有了派生类指针转换为基类指针。之后,使用dynamic_cast,把基类指针转换派生类指针。

dynamic_cast的作用:

作用:将基类类型的指针或引用安全地转换为派生类型的指针或引用。

注意事项:

(0)在dynamic_cast中,dynamic表示在类型转换时,是在运行时进行的,其与Static对应。

(1)使用dynamic_cast转换时,在expression中必有虚函数,否则编译会出错。而Static_cast没有这个限制。但是即使原来的参数和目标参数之间没有继承关系时,编译器不会报错。

原因:在类中存在虚函数时,才会有可能出现让基类指针或引用指向派生类对象的情况(虚函数使用的前提),此时转换才有意义。或者说,dynamic_cast操作时专门针对由虚函数的继承来的,它将基类指针转换为想要的子类指针,以做好操作子类中有而父类中没有的操作。由于,判断类是否有继承关系,是需要检测是否有虚函数,即在原来的参数和目标参数之间没有继承关系时,编译器不会报错,因为它由虚函数。

dynamic_cast的语法:

dynamic_cast<type-id>(expression)

说明:

(1) type_id 和 目标类型是一样的

(2) type_id 和
目标类型
是引用或指针。

举例:

Derived* pDerived = dynamic_cast<Derived*>(pBaseOne);//成功,返回Derived的指针,失败返回NULL
Base& BaseClass = dynamic_cast<Base&>(DerivedClass); //成功,返回Base的引用,失败抛出bad_cast异常

使用dynamic_cast转换时,会执行两个操作:

(1)它首先验证被请求的转换是否有效,这是在编译时判断,检测expression代表的类中是否有虚函数。

(2)对操作数进行类型转换。这是在运行是判断。

dynamic_cast的分类:

dynamic_cast要求要转换的两个值是有关系的,如果转型成功,则返回转换过的指针,如果转换不成功,返回NULL。

根据类之间的关系,可以分三种情况:

(1)上行转换:一个子类对象的指针/引用转换为基类的指针/引用(子类到基类的转换)

(2)下行转换:基类的指针/引用转换为一个子类的指针/引用(基类到子类的转换)

(3)交叉转换:多个基类,不同子类之间的相互转换

下面分情况说明这三种情况:

(1)上行转换:一个子类对象的指针/引用转换为基类的指针/引用(子类到基类的转换)

说明:这里dynamic_cast和static_cast以及直接赋值的效果是一样的。

举例:

#include <iostream>
using namespace std;

class Base
{
public:
	virtual void BaseAAA()
	{
		cout<<"BaseAAA"<<endl;
	}

	virtual void AAA()
	{
		cout<<"BaseAAA::VirtualAAA"<<endl;
	}
};

class Derived : public Base
{
public:
	virtual void DerivedAAA()
	{
		cout<<"DerivedAAA"<<endl;
	}

	virtual void AAA()
	{
		cout<<"Derived::VirtualAAA"<<endl;
	}
};



int main()
{
	Derived* pD = new Derived;
	
	//正常情况-dynamic_cast
	Base* pB = dynamic_cast<Derived*>(pD);
	pB->BaseAAA(); //输出BaseAAA
	pB->AAA(); //输出Derived::VirtualAAA

	//正常情况-static_cast
	Base* pBB = static_cast<Derived*>(pD);
	pBB->BaseAAA(); //输出BaseAAA
	pBB->AAA(); //输出Derived::VirtualAAA

	//不要使用转换
	Base* pBBB = pD;
	pBBB->BaseAAA(); //输出BaseAAA
	pBBB->AAA(); //输出Derived::VirtualAAA

	system("pause");
	return 1;
}

(2)下行转换:基类的指针/引用转换为一个子类的指针/引用(基类到子类的转换)

说明:这是dynamic_cast的特长。比static_cast安全。

原因:dynamic_cast能够在运行是对类型进行检查,如果绑定到引用或指针的对象不是目标类型的对象,则dynamic_cast 失败。

dynamic_cast在什么时候能成功?

如果基类指针或引用本来原来是指向或引用一个派生类的情况下,会成功。

失败后的行为:

使用dynamic_cast转换:

如果,基类指针或引用的值在转换前就是指向或引用派生类的情况下,则在转换后,派生类指针或引用就重新获得了一个派生类指针或引用。

如果,基类指针或引用的值在转换前就是指向或引用基类的情况下,在语句执行时,会立即出现错误,并返回不同的状态。

dynamic_cast失败后的状态:

如果,待转换的参数为指针类型,则返回NULL。

如果,待转换的参数为引用类型,则抛出一个bad_cast 类型的异常。

使用static_cast转换:

如果,基类指针或引用的值在转换前就是指向或引用派生类的情况下,则在转换后,派生类指针或引用就重新获得了一个派生类指针或引用。

如果,基类指针或引用的值在转换前就是指向或引用基类的情况下,在语句执行时,不会立即出现错误,而是会在使用的时候才会出现错误。

static_cast失败的情况:

如果,使用转换后的指针或引用调用了派生类的成员,则会报错。

如果,仍然调用基类(自己)的成员,则不会报错。即会隐含错误。

举例:

#include <iostream>
using namespace std;

class Base
{
public:
	virtual void BaseAAA()
	{
		cout<<"BaseAAA"<<endl;
	}

	virtual void AAA()
	{
		cout<<"BaseAAA::VirtualAAA"<<endl;
	}
};

class Derived : public Base
{
public:
	virtual void DerivedAAA()
	{
		cout<<"DerivedAAA"<<endl;
	}

	virtual void AAA()
	{
		cout<<"Derived::VirtualAAA"<<endl;
	}
};
int main()
{
	Derived* pD = NULL;
	Base* pB = new Derived;

	//正常情况-dynamic_cast
	pD = dynamic_cast<Derived*>(pB);
	pD->DerivedAAA(); //输出DerivedAAA
	pD->AAA(); //输出Derived::VirtualAAA

	//正常情况-static_cast
	Derived* pD1 = static_cast<Derived*>(pB);
	pD1->DerivedAAA(); //输出DerivedAAA
	pD1->AAA(); //输出Derived::VirtualAAA

	
	Base* pBB = new Base;
	
	//失败情况
	Derived* pDD = dynamic_cast<Derived*>(pBB);//此时pDD返回NULL
	//pDD->DerivedAAA(); //出错
	//pDD->AAA(); //出错

	//失败情况-static_cast
	Derived* pDD1 = static_cast<Derived*>(pBB);
	//pDD1->DerivedAAA(); //出错-用到不是自己的成员时,且该成员是虚函数时才报错。
	pDD1->AAA(); //输出Base::VirtualAAA 

	system("pause");
	return 1;
}

B要有虚函数,否则会编译出错;static_cast则没有这个限制。

B中需要检测有虚函数的原因:类中存在虚函数,就说明它有想要让基类指针或引用指向派生类对象的情况,此时转换才有意义。
这是由于运行时类型检查需要运行时类型信息,而这个信息存储在类的虚函数表(关于虚函数表的概念,详细可见<Inside c++ object model>)中,只有定义了虚函数的类才有虚函数表,没有定义虚函数的类是没有虚函数表的。

(3)交叉转换:多个基类,不同子类之间的相互转换

说明:dynamic_cast可以做,但是static_cast不可以做,编译都通不过。

举例:


dynamic_cast在什么时候能成功转换?

如果一个派生类DerivedTwo是由两个基类BaseOne和BaseTwo派生。

而且把DerivedTwo对象的指针给了其中一个基类BaseOne,那么可以对BaseOne的指针进行dynamic_cast转换,转换为BaseTwo的指针。

BaseOne* pBaseOne = new DerivedTwo;
BaseTwo* pBaseTwo = dynamic_cast<BaseTwo*>(pBaseOne);
pBaseTwo->AAA();  //正确执行
pBaseTwo->BaseTwoAAA(); //正确执行

为什么dynamic_cast能成功转换?

这是由于在定义指针pBaseOne的指向时,其指向的内容本来就包含两个基类的内容,所以可以转换成功。

给出不成功的情况:

BaseOne* pBaseOne = new DerivedOne;
BaseTwo* pBaseTwo = dynamic_cast<BaseTwo*>(pBaseOne);
pBaseTwo->AAA();//报错
pBaseTwo->BaseTwoAAA();//报错

dynamic_cast不能转换的原因?

这是由于在定义指针pBaseOne的指向时,其指向的内容只包含一个基类BaseOne的内容,不含有BaseTwo的内容,所以不能成功,此时pBaseTwo的值为NULL。

举例:

#include <iostream>
using namespace std;

class BaseOne
{
public:
	virtual void BaseOneAAA()
	{
		cout<<"BaseOneAAA"<<endl;
	}

	virtual void AAA()
	{
		cout<<"BaseOne::VirtualAAA"<<endl;
	}
};

class BaseTwo
{
public:
	virtual void BaseTwoAAA()
	{
		cout<<"BaseTwoAAA"<<endl;
	}

	virtual void AAA()
	{
		cout<<"BaseTwo::VirtualAAA"<<endl;
	}
};

class DerivedOne : public BaseOne
{
public:
	virtual void DerivedOneAAA()
	{
		cout<<"DerivedOneAAA"<<endl;
	}

	void AAA()
	{
		cout<<"DerivedOne::VirtualAAA"<<endl;
	}
};

class DerivedTwo : public BaseOne,public BaseTwo
{
public:
	virtual void DerivedTwoAAA()
	{
		cout<<"DerivedTwoAAA"<<endl;
	}

	void AAA()
	{
		cout<<"DerivedTwo::VirtualAAA"<<endl;
	}
};
int main()
{
	BaseOne* pBaseOne = NULL;
	BaseTwo* pBaseTwo = NULL;

	//dynamic_cast转换成功的情况
	pBaseOne = new DerivedTwo;
	pBaseTwo = dynamic_cast<BaseTwo*>(pBaseOne);
	pBaseTwo->AAA();  //输出DerivedTwo::VirtualAAA
	pBaseTwo->BaseTwoAAA(); //输出BaseTwoAAA

	//dynamic_cast转换失败的情况
	pBaseOne = new DerivedOne;
	pBaseTwo = dynamic_cast<BaseTwo*>(pBaseOne);
	pBaseTwo->AAA();
	pBaseTwo->BaseTwoAAA();

	////static_cast转换失败的情况
	////直接是编译不通过:static_cast无法实现BaseOne* 到BaseTwo* 的转换。

	//pBaseOne = new DerivedOne;
	//pBaseTwo = static_cast<BaseTwo*>(pBaseOne);
	//pBaseTwo->AAA();
	//pBaseTwo->BaseTwoAAA();

	//pBaseOne = new DerivedTwo;
	//pBaseTwo = static_cast<BaseTwo*>(pBaseOne);
	//pBaseTwo->AAA(); 
	//pBaseTwo->BaseTwoAAA(); 


	system("pause");
	return 1;
}

参考文章:http://www.cnblogs.com/weidagang2046/archive/2010/04/10/1709226.html

抱歉!评论已关闭.