c++类型转换

参考:http://www.cnblogs.com/goodhacker/archive/2011/07/20/2111996.html

http://www.cnblogs.com/ider/archive/2011/08/01/cpp_cast_operator_part5.html

c++中有四种运算符用于类型转换:

const_cast,字面上理解就是去const属性。
static_cast,命名上理解是静态类型转换。如int转换成char。
dynamic_cast,命名上理解是动态类型转换。如子类和父类之间的多态类型转换。
reinterpret_cast,仅仅重新解释类型,但没有进行二进制的转换。

static_cast
类似于C风格的强制转换。无条件转换,静态类型转换。用于:
1.基类和子类之间转换:其中子类指针转换成父类指针是安全的;但父类指针转换成子类指针是不安全的。(基类和子类之间的动态类型转换建议用dynamic_cast)
2. 基本数据类型转换。enum, struct, int, char, float等。static_cast不能进行无关类型(如非基类和子类)指针之间的转换。
3. 把空指针转换成目标类型的空指针。
4. 把任何类型的表达式转换成void类型。
5. static_cast不能去掉类型的const、volitale属性(用const_cast)。
dynamic_cast
dynamic_cast依赖于RTTI信息,其次,在转换时,dynamic_cast会检查转换的source对象是否真的可以转换成target类型,这种检查不是语法上的,而是真实情况的检查。先看RTTI相关部分,通常,许多编译器都是通过vtable找到对象的RTTI信息的,这也就意味着,如果基类没有虚方法,也就无法判断一个基类指针变量所指对象的真实类型,这时候,dynamic_cast只能用来做安全的转换,例如从派生类指针转换成基类指针.而这种转换其实并不需要dynamic_cast参与。dynamic_cast 主要用于执行“安全的向下转型(safe downcasting)”,也就是说,要确定一个对象是否是一个继承体系中的一个特定类型。

有条件转换,动态类型转换,运行时类型安全检查(转换失败返回NULL):
1. 安全的基类和子类之间转换。
2. 必须要有虚函数。
3. 相同基类不同子类之间的交叉转换。但结果是NULL。

含有虚函数:

class Base{
public:
	virtual void fun(){}
};
class Derived :public Base{
};

int _tmain(int argc, _TCHAR* argv[])
{
	Base* pb = 0;
	Derived* pd = new Derived();
	pb = pd;
	cout << typeid(*pb).name() << endl;
	pb = dynamic_cast<Base*>(pd);
	cout << typeid(*pb).name() << endl;

	Derived d2 = Derived();
	Base& b = d2;
	cout << typeid(b).name() << endl; 
	Base& b2 = dynamic_cast<Base&>(d2);
	cout << typeid(b2).name() << endl;
	return 0;
}
运行结果:例子中动态转换和隐式转换都是向上转换,因为有虚函数,所有都转换成功。

class Derived
class Derived
class Derived
class Derived
如果将虚函数“virtual void fun(){}”注释,运行结果如下,没有发生动态转换:
class Base
class Base
class Base
class Base

向下转换,如果虚函数存在:

Base* pb = new Base();
Derived* pd = 0;
pd = dynamic_cast<Derived*>(pb);
cout << pd << endl;
运行结果如下,由于pb指向的真实类型为Base,所以向下转换不成功,返回null,所以dynamic_cast向下转换是安全的。

00000000
向下转换,虚函数同样存在,如果将基类指针指向子类对象,然后再进行向下动态转换:
Base* pb = new Base();
Derived* pd = 0;
pb = new Derived();
pd = dynamic_cast<Derived*>(pb);
cout << pd << endl;
运行结果如下,向下转换成功:
003AD318
向下转换,如果不存在虚函数,编译器直接拒绝动态转换,导致编译无法通过,报错 'dynamic_cast' : 'Base' is not a polymorphic type

reinterpret_cast
仅仅重新解释类型,但没有进行二进制的转换:
1. 转换的类型必须是一个指针、引用、算术类型、函数指针或者成员指针。
2.在比特位级别上进行转换。它可以把一个指针转换成一个整数,也可以把一个整数转换成一个指针(先把一个指针转换成一个整数,在把该整数转换成原类型的指针,还可以得到原先的指针值)。但不能将非32bit的实例转成指针。
3. 最普通的用途就是在函数指针类型之间进行转换。
4. 很难保证移植性。

dynamic_cast运算符,应该算是四个里面最特殊的一个,因为它涉及到编译器的属性设置,而且牵扯到的面向对象的多态性跟程序运行时的状态也有关系,所以不能完全的使用传统的转换方式来替代。但是也因此它是最常用,最不可缺少的一个运算符。与static_cast一样,dynamic_cast的转换也需要目标类型和源对象有一定的关系:继承关系。更准确的说,dynamic_cast是用来检查两者是否有继承关系。因此该运算符实际上只接受基于类对象的指针和引用的类转换。从这个方面来看,似乎dynamic_cast又和reinterpret_cast是一致的,但实际上,它们还是存在着很大的差别。

#include "stdafx.h"
#include <iostream>
#include <string>
using namespace std;
class CParent{
public:
	CParent(const string& name = "Parent"):strName(name)
	{
		cout << "This is parent constructor." << endl;
	}
	virtual ~CParent(){}
	virtual void speak(){
		cout << "\tParent: I am " << strName << " who is speaking."<<endl;
	}
	void work(){
		cout << "\tParent: I am " << strName << " who is working." << endl;
	}
protected:
	string strName;
};
class CChild : public CParent{
public:
	CChild(const string& name = "Child") :CParent(name), nTest(0), nTest1(1), nTest2(2){
		cout << "This is child constructor." << endl;
	}
	virtual ~CChild(){}
	virtual void speak(){
		nTest = 0;
		cout << "\tChild: I am " << strName << " who is speaking." << endl;
	}
	void study(){
		myName = "ss";
		cout << nTest<<endl;
		cout << nTest1<<endl;
		cout << nTest2<<endl;
		cout << "\tChild: I am " << strName << " who is studying." << endl;
	}
private:
	string myName;
	int nTest;
	int nTest1;
	int nTest2;
};

class CStranger{
public:
	CStranger(const string& name = "Stranger") :strName(name)
	{
		cout << "This is stranger constructor." << endl;
	}
	virtual ~CStranger(){}
	void selfIntroduction(){
		cout << "\tStranger: I am stranger."<<endl;
	}
	void speak(){
		cout << "\tStranger: I am " << strName << " who is speaking." << endl;
	}
protected:
	string strName;
};
int _tmain(int argc, _TCHAR* argv[])
{
	cout << "dynamic_cast from child class to base class:" << endl;
	CChild * daughter_d = new CChild("Daughter");
	CParent * mother_d = dynamic_cast<CParent*> (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;
	CChild * son_s = new CChild("Son");
	CParent * father_s = static_cast<CParent*> (son_s); //right, cast with polymorphism
	father_s->speak();
	father_s->work();

	cout << "dynamic_cast from base class to child class:" << endl;
	CParent * father_d = new CParent("Father");
	CChild * son_d = dynamic_cast<CChild*> (father_d); //no error, but not safe
	if (son_d)
	{
		son_d->speak();
		son_d->study();
	}
	else cout << "\t[null], can not dynamic_cast from base class to child class." << endl;

	cout << "static_cast from base class to child class:" << endl;
	CParent * mother_s = new CParent("Mother");
	CChild * daughter_s = static_cast<CChild*> (mother_s);  //no error, but not safe
	if (daughter_s)
	{
		daughter_s->speak();
		daughter_s->study();
	}
	else cout << "\t[null], can not static_cast from base class to child class." << endl;

	cout << "dynamic_cast to non-related class:" << endl;
	CStranger* stranger_d = dynamic_cast<CStranger*> (daughter_d);
	if (stranger_d)
	{
		stranger_d->selfIntroduction();
		stranger_d->speak();
	}
	else cout << "\t[null], can't dynamic_cast to non-related class." << endl;

	cout << "reinterpret_cast to non-related class:" << endl;
	CStranger* stranger_r = reinterpret_cast<CStranger*> (son_s);
	if (stranger_r)
	{
		stranger_d->selfIntroduction();
		//stranger_d->speak();	//This line would cause program crash,
		//as "name" could not be found corretly.
	}
	else cout << "\t[null], can't reinterpret_cast to non-related class." << endl;

	cout << "use dynamic_cast to cast back from static_cast:" << endl;
	CChild* child_s = dynamic_cast<CChild*> (father_s);
	if (child_s)
	{
		child_s->speak();
		child_s->work();
	}
	else cout << "\t[null], can't dynamic_cast to cast back from static_cast." << endl;

	cout << "use dynamic_cast to cast back from reinterpret_cast:" << endl;
	CChild* child_r = dynamic_cast<CChild*> (stranger_r);
	if (child_r)
	{
		child_r->speak();
		child_r->work();
	}
	else cout << "\t[null], dynamic_cast to cast back from reinterpret_cast" << endl;

	return 0;
}
/*****************************************Result***********************************/
dynamic_cast from child class to base class:
This is parent constructor.
This is child constructor.
        Child: I am Daughter who is speaking.
        Parent: I am Daughter who is working.
static_cast from child class to base class:
This is parent constructor.
This is child constructor.
        Child: I am Son who is speaking.
        Parent: I am Son who is working.
dynamic_cast from base class to child class:
This is parent constructor.
        [null], can not dynamic_cast from base class to child class.
static_cast from base class to child class:
This is parent constructor.
        Parent: I am Mother who is speaking.
0
0
0
        Child: I am Mother who is studying.
dynamic_cast to non-related class:
        [null], can't dynamic_cast to non-related class.
reinterpret_cast to non-related class:
        Stranger: I am stranger.
use dynamic_cast to cast back from static_cast:
        Child: I am Son who is speaking.
        Parent: I am Son who is working.
use dynamic_cast to cast back from reinterpret_cast:
        [null], dynamic_cast to cast back from reinterpret_cast
结果分析:

对于从子类到基类的指针转换,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一样造成的程序的崩溃,只是类的方法都只有一份,只有数据成员属于对象,所以在调用那些不会访问对象的数据的方法时(如CStranger的selfIntroduction())并不会造成崩溃。而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)来判定的,它还受到编译器的影响,有些编译器需要设置开启才能让程序正确运行



  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值