C++中的类型转换运算符dynamic_cast、static_cast、const_cast和reinterpret_cast详解

零、小序

C++程序员都知道,C语言中的强制转换和万能转换指针“void*”很常用,然而这样的转换有时候很不安全,甚至有时候是无意义的转换!C风格的强制转换和万能转换都需要程序员自己把握转换的安全性,出现错误的可能性很大。面对C风格的松散转换,C++增加了4个类型转换运算符,用于规范类型转换,程序员可以根据需要选择一个类型转换运算符,并让编译器去检查类型转换的安全性,提高代码的安全性和健壮性!

一、dynamic_cast

1、关于dynamic_cast

dynamic_cast运算符是C++程序员经常使用的转换类型,它也是最常用的RTTI(运行阶段类型识别)组件,是基于类的继承层次之间一种动态的转换,这就要求基类必须有虚函数才能和子类之间进行转换。

它既允许向上转换(子类转换为父类)、也允许向下转换(父类转换为子类)。只有指针类型与转换的对象类型(或者是对象的直接或间接的基类)相同时才一定是安全的。因此向上转换是安全的,而向下转换不一定安全。转换不成功时,指针会返回空值。

dynamic_cast也可以用于引用的转换,但是由于没有和空指针对应的引用值,当引用转换不正确时,会抛出异常。

2、代码示例

// DynamicCastType.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
//

#include <iostream>

using namespace std;

class Grand
{
public:
	Grand() {}
	~Grand() {}

	virtual void printName()
	{
		cout << "I'm Grand!" << endl;
	}
private:

};

class Son : public Grand
{
public:
	Son() {}
	~Son() {}
	virtual void printName()
	{
		cout << "I'm Son!" << endl;
	}

	void sayName()
	{
		cout << "I'm Jack!" << endl;
	}

private:

};

class GrandSon : public Son
{
public:
	GrandSon() {}
	~GrandSon() {}
	virtual void printName()
	{
		cout << "I'm GrandSon!" << endl;
	}

	void sayAge()
	{
		cout << "My age is 18!" << endl;
	}
private:

};

#define DELETE_PTR(p) {if(p!=nullptr){delete (p); (p)=nullptr;}}

int main()
{
	cout << "-----------------dynamic_cast类型转换------------------" << endl;
	Grand *pGrand = new Grand;
	Son *pSon = new Son;
	GrandSon *pGrandSon = new GrandSon;
	cout << "-----------------向上转换安全------------------" << endl;
	Grand *pTmpGrand1 = dynamic_cast<Grand*>(pSon); // 向上转换安全
	pTmpGrand1->printName();
	Grand *pTmpGrand2 = dynamic_cast<Grand*>(pGrandSon); // 向上转换安全
	pTmpGrand2->printName();
	Son *pTmpGrandSon1 = dynamic_cast<GrandSon*>(pGrandSon); // 向上转换安全
	pTmpGrandSon1->printName();

	cout << "-----------------向下转换不一定安全------------------" << endl;
	Son *pSon1 = dynamic_cast<Son*>(pGrand);
	if (pSon1 != nullptr)
	{
		pSon1->sayName();
	}
	else
	{
		cout << "指针pSon1是空的!" << endl;
	}
	GrandSon *pGrandSon1 = dynamic_cast<GrandSon*>(pGrand);
	if (pGrandSon1 != nullptr)
	{
		pGrandSon1->sayAge();
	}
	else
	{
		cout << "指针pGrandSon1是空的!" << endl;
	}
	
	// 只需要释放这三个指针就行,一块内存只允许释放一次,其他指针都是由这三个指针转换的,地址都是同一块
	DELETE_PTR(pGrand);
	DELETE_PTR(pSon);
	DELETE_PTR(pGrandSon);

    std::cout << "Hello World!\n";

	getchar();
}

运行结果:
在这里插入图片描述

二、static_cast

1、关于static_cast

static_cast运算符也是C++程序员经常使用的转换类型,与dynamic_cast相比,它是一种静态的转换,使用它在基类和子类之间的互相转换是合法的,但合法并不代表安全!

它像dynamic_cast一样既允许向上转换(子类转换为父类)、也允许向下转换(父类转换为子类)。只要相互转换的类型有关系(直接或者间接继承)转换都是合法的,切记合法不一定安全!它的使用接近C风格的强制类型转换,安全性需要程序员自己把握。

由于static_cast是无需进行类型转换的,所以它可以将枚举值转换成整型,也可以将整型转换为枚举值;同样可以将doube、int、float、long等各种数值之间进行转换,但要注意转换精度,确保是自己需要的转换。

2、代码示例

// StaticCastType.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
//

#include <iostream>

using namespace std;

class Grand
{
public:
	Grand() {}
	~Grand() {}

	virtual void printName()
	{
		cout << "I'm Grand!" << endl;
	}
private:

};

class Son : public Grand
{
public:
	Son() {}
	~Son() {}
	virtual void printName()
	{
		cout << "I'm Son!" << endl;
	}

	void sayName()
	{
		cout << "I'm Jack!" << endl;
	}

private:

};

class GrandSon : public Son
{
public:
	GrandSon() {}
	~GrandSon() {}
	virtual void printName()
	{
		cout << "I'm GrandSon!" << endl;
	}

	void sayAge()
	{
		cout << "My age is 18!" << endl;
	}
private:

};

#define DELETE_PTR(p) {if(p!=nullptr){delete (p); (p)=nullptr;}}

int main()
{
	cout << "-----------------static_cast类型转换------------------" << endl;
	cout << "-----------------static_cast类型用于指针转换------------------" << endl;
	Grand *pGrand = new Grand;
	Son *pSon = new Son;
	GrandSon *pGrandSon = new GrandSon;
	cout << "-----------------向上转换安全------------------" << endl;
	Grand *pTmpGrand1 = static_cast<Grand*>(pSon); // 向上转换安全
	pTmpGrand1->printName();
	Grand *pTmpGrand2 = static_cast<Grand*>(pGrandSon); // 向上转换安全
	pTmpGrand2->printName();
	Son *pTmpGrandSon1 = static_cast<GrandSon*>(pGrandSon); // 向上转换安全
	pTmpGrandSon1->printName();

	cout << "-----------------向下转换不一定安全------------------" << endl;
	Son *pSon1 = static_cast<Son*>(pGrand);
	if (pSon1 != nullptr)
	{
		pSon1->sayName();
	}
	else
	{
		cout << "指针pSon1是空的!" << endl;
	}
	GrandSon *pGrandSon1 = static_cast<GrandSon*>(pGrand);
	if (pGrandSon1 != nullptr)
	{
		pGrandSon1->sayAge();
	}
	else
	{
		cout << "指针pGrandSon1是空的!" << endl;
	}
	cout << "-----------------static_cast类型用于数值转换------------------" << endl;
	int iNum = 100;
	float fNum = 150.5;
	short sNum = static_cast<short>(iNum);
	int iTmpNum = static_cast<int>(fNum);
	double dNum = static_cast<double>(iNum);
	cout << "iNum=" << iNum << endl;
	cout << "fNum=" << fNum << endl;
	cout << "sNum=" << sNum << endl;
	cout << "iTmpNum=" << iTmpNum << endl;
	cout << "dNum=" << dNum << endl;

	// 只需要释放这三个指针就行,一块内存只允许释放一次,其他指针都是由这三个指针转换的,地址都是同一块
	DELETE_PTR(pGrand);
	DELETE_PTR(pSon);
	DELETE_PTR(pGrandSon);
	

	std::cout << "Hello World!\n";

	getchar();
}

运行结果:
在这里插入图片描述

三、const_cast

1、关于const_cast

const_cast运算符的只有一个作用就是去const化,将一个常量类型转换为一个可以修改其他值的非常量类型。

这个运算符使用的场景是,需要一个值大多数时候都是不可变的,把这个值声明为const类型,而在某种时刻又是需要可以修改的,此时可以使用const_cast进行修改它。

2、代码示例

// ConstCastType.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
//

#include <iostream>
using namespace std;

class Grand
{
public:
	Grand() {}
	~Grand() {}

	virtual void printName()
	{
		cout << "I'm Grand!" << endl;
	}
private:

};

class Son : public Grand
{
public:
	Son() {}
	~Son() {}
	virtual void printName()
	{
		cout << "I'm Son!" << endl;
	}

	void sayName()
	{
		cout << "I'm Jack!" << endl;
	}

private:

};

#define DELETE_PTR(p) {if(p!=nullptr){delete (p); (p)=nullptr;}}

int main()
{
	cout << "-----------------const_cast类型转换------------------" << endl;
	const char * cStr = "ISmileLI";
	char* cTmpStr = const_cast<char*>(cStr); // cStr本身是个常量没有变,它的返回值已经是一个非常量
	cout << "cStr:" << cStr << endl;
	cout << "cTmpStr:" << cTmpStr << endl;

	cout << "-----------------const_cast允许去掉一个类对象的const------------------" << endl;
	const Grand *pGrand = new Grand;
	Grand *pTmpGrand = const_cast<Grand *>(pGrand);
	if (pTmpGrand != nullptr)
	{
		pTmpGrand->printName();
	}
	else
	{
		cout << "指针pTmpGrand是空的!" << endl;
	}
	cout << "-----------------const_cast不允许转换类对象的类型------------------" << endl;
	const Son *pSon = new Son;
	//Grand *pTmpGrand2 = const_cast<Grand *>(pSon); // 编译器会提示错误
	cout << "//Grand *pTmpGrand2 = const_cast<Grand *>(pSon); // 编译器会提示错误" << endl;
	
	// const类型的指针的内存空间不需要手动释放,所以不需要调用下面的代码
	//DELETE_PTR(cStr);
	//DELETE_PTR(pGrand);

	std::cout << "Hello World!\n";
	getchar();
}

运行结果:
在这里插入图片描述

四、reinterpret_cast

1、关于reinterpret_cast

reinterpret_cast运算符是这四种转换运算符中最不安全的、风险最大的转换类型,它主要适用于依赖底层实现的编程技术,因为不系统一个类型存储的字节可能是不一样的,所以具有不可移植性、对平台的依赖性强。

存在即合理!它可以对无关类指针进行转换,甚至可以直接将整型值转成指针,也可以将指针转换成足以存储指针的整型。

2、代码示例

// ReinterpretCastType.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
//

#include <iostream>
using namespace std;

class  Data
{
public:
	short a;
	short b;
};

#define DELETE_PTR(p) {if(p!=nullptr){delete (p); (p)=nullptr;}}
int main()
{
	cout << "-----------------reinterpret_cast类型转换------------------" << endl;
	long value = 0xA222B111;
	Data *pData = reinterpret_cast<Data*>(&value);
	cout << "pData->a的十六进制输出:"<< hex << pData->a << endl;
	cout << "pData->b的十六进制输出:" << hex << pData->b << endl;
	// 不需要释放指针pData的空间,它的地址和变量value相同,会自动释放掉
	// DELETE_PTR(pData);
    std::cout << "Hello World!\n";
	getchar();
}

运行结果:
在这里插入图片描述

五、总述

dynamic_cast:主要用于动态类型转换,可以进行向上和向下转换,向上转换是安全的,向下转换不一定安全,转换失败会返回空指针或者抛出异常。
static_cast:主要用于静态类型转换,可以进行向上和向下转换,向上或者向下转换都是合法的,但合法并不代表安全!安全性需要程序员自己把握,类似于C风格的强制转换。
const_cast:只有一个作用就是去const化,将一个常量类型转换为一个可以修改其他值的非常量类型。
reinterpret_cast:最不安全的、风险最大的转换类型,它主要适用于依赖底层实现的编程技术,因为不系统一个类型存储的字节可能是不一样的,所以具有不可移植性、对平台的依赖性强。它可以对无关类指针进行转换,甚至可以直接将整型值转成指针,也可以将指针转换成足以存储指针的整型。

原创不易,点赞鼓励一下吧!

posted @ 2020-04-04 15:28  ISmileLi  阅读(43)  评论(0编辑  收藏  举报