C++四种类型转换运算符const_cast、static_cast、dynamic_cast、reinterpret_cast相关实验总结

1.const_cast<type-name>(expression)

目标类型只能是指针或者引用,作用是去掉类型的const或volatile属性

2.static_cast<type-name>(expression)

类似于C风格的强制转换,无条件转换,但没有运行时类型检查来保证转换的安全性,所以在编译时已经完成转换。
    static_cast一般用于基本类型转换。
    1)基类和子类之间的转换:其中子类指针转换为父类指针是安全的,但父类指针转换为子类指针是不安全的(基类和子类之间的动态类型转换建议用dynamic_cast)。
    基类与子类之间的转换是可以编译成功的,但从上向下转换不安全。
    2)基本数据类型转换,enum,struct,int,char,float等。
    3)static_cast不能进行无关类型(如非基类和子类)指针之间的转换,编译时直接报错,包括同一基类的其他子类之间转换都会编译报错。
    4)把任何类型的表达式转换成void类型。
    5)static_cast不能去掉类型的const、volatile属性(用const_cast)。

3.dynamic_cast<type-name>(expression)

主要用于多态类之间的类型转换,类层次间的上行转换和下行转换。
    动态类型转换,运行时检查类型安全(转换失败返回NULL),是一种RTTI(Run-Time Type Identification)-运行时类型识别机制:
    1)dynamic_cast是运行时处理的,运行时要进行类型检查。
    2)不能用于内置的基本数据类型的强制转换。
    3)dynamic_cast转换的目标类型必须是指针或引用。dynamic_cast转换如果成功的话返回的是指向类的指针或引用,指针转换失败的话则会返回nullptr;
    因为不存在所谓空引用,所以引用类型的dynamic_cast转换与指针类型不同,在引用转换失败时,会抛出std::bad_cast异常,该异常定义在头文件typeinfo中。
    4)在使用dynamic_cast转换时,在类层次间进行上行转换(子类指针指向父类指针)时,dynamic_cast和static_cast 的效果是一样的。
    在进行下行转换(父类指针转化为子类指针)时,dynamic_cast具有类型检查的功能,比static_cast 更安全。向下转换的成功与否还与将要转换的类型有关,
    即要转换的指针指向的对象的实际类型与转换以后的对象类型一定要相同,否则转换失败。在C++中,编译期的类型转换有可能会在运行时出现错误,
    特别是涉及到类对象的指针或引用操作时,更容易产生错误。Dynamic_cast操作符则可以在运行期对可能产生问题的类型转换进行测试。
    5)使用dynamic_cast进行转换的,基类中一定要有虚函数,否则编译不通过;向下转换时,在编译时就会校验包含虚函数的继承关系。

4.reinterpret_cast<type-name>(expression)

    不同类型的指针类型转换用reinterpret_cast。
    1)转换的类型必须是一个指针,应用、算术类型、函数指针或者成员指针。
    2)在比特级别上进行转换,可以把一个指针转换成一个整数,也可以把一个整数转换成一个指针(先把一个指针转换成一个整数,
    在把该整数转换成原类型的指针,还可以得到原先的指针值)。但不能将非32bit的实例转成指针。
    3)最普通的用途就是在函数指针类型之间进行转换。
    4)reinterpret_cast意图执行低级转型,实际动作(及结果)可能取决于编辑器,这也就表示它不可移植。

#include <string>
#include <iostream>
#include <typeinfo>

using namespace std;

/*
	const_cast<type-name>(expression):目标类型只能是指针或者引用,作用是去掉类型的const或volatile属性
*/
void Test_Const_Cast()
{
	std::cout << __FUNCTION__ << ":" << std::endl;

	class A 
	{
	public:
		A(int v) : value(v){}
	public:
		int value;
	};

	const A a = { 10 };
	//A a1 = const_cast<A>(a);		// 编译报错,const_cast转换目标类型必须是引用或者指针
	//a1.value = 20;

	A& a2 = const_cast<A&>(a);		// const_cast去掉const属性转换
	a2.value = 20;
	std::cout << a.value << ' ' << a2.value << std::endl;

	A* a3 = const_cast<A*>(&a);		// const_cast去掉const属性转换
	a3->value = 30;
	std::cout << a.value << ' ' << a3->value << std::endl;

	const A* p_a = new A(10);
	A* p_a1 = const_cast<A*>(p_a);	// const_cast去掉const属性转换
	p_a1->value = 40;
	std::cout << p_a->value << ' ' << p_a1->value << std::endl;
	delete p_a;
	p_a = nullptr;

	std::cout << std::endl;
}

/*
	static_cast<type-name>(expression):把expression转换为type-name类型,但没有运行时类型检查来保证转换的安全性,所以在编译时已经完成转换。
	static_cast一般用于基本类型转换。
	1)基类和子类之间的转换:其中子类指针转换为父类指针是安全的,但父类指针转换为子类指针是不安全的(基类和子类之间的动态类型转换建议用dynamic_cast)。
	基类与子类之间的转换是可以编译成功的,但从上向下转换不安全。
	2)基本数据类型转换,enum,struct,int,char,float等。
	3)static_cast不能进行无关类型(如非基类和子类)指针之间的转换,编译时直接报错,包括同一基类的其他子类之间转换都会编译报错。
	4)把任何类型的表达式转换成void类型。
	5)static_cast不能去掉类型的const、volatile属性(用const_cast)。
*/
void Test_Static_Cast()
{
	std::cout << __FUNCTION__ << ":" << std::endl;

	std::cout << "base type:" << std::endl;
	const int a1 = 1;	// int
	const int a2 = 4;	// int
	auto a3 = static_cast<double>(a1) / static_cast<double>(a2);
	cout << typeid(a3).name() << endl;			// double
	//int* p_a = static_cast<int*>(&a1);		// 编译报错,static_cast不能去掉const限定符

	class A{
	public:
		void f()
		{
			cout << __FUNCTION__ << ":" << i_value << endl;
		}
		int i_value = 12;
	};
	class B : public A {
	public:
		void g(){
			cout << __FUNCTION__ << ":" << str_value << endl;
		}
		string str_value = "123456";
	};

	std::cout << "A-B reference:" << std::endl;
	A a;
	B b;
	auto& b_a = static_cast<A&>(b);					// 向上转换是安全的
	cout << typeid(b).name() << endl;				// B
	cout << typeid(b_a).name() << endl;				// A

	auto& b_a_b = static_cast<B&>(b_a);				// 编译OK,向下转换不安全,建议使用dynamic_cast
	cout << typeid(b_a_b).name() << endl;			// B

	auto& a_b = static_cast<B&>(a);					// 编译OK,向下转换不安全,建议使用dynamic_cast
	cout << typeid(a).name() << endl;				// A
	cout << typeid(a_b).name() << endl;				// B
	a_b.f();
	//a_b.g();										// 这里调用会出现不可预知的异常,因为变量a内存布局本来是A类型的,强制转换为B后,
													// 内存布局中不能通过g函数访问B类型成员变量str_value,最后会导致崩溃

	std::cout << "A-B pointer:" << std::endl;
	A* p_a = new A;
	B* p_b = new B;
	auto p_b_a = static_cast<A*>(p_b);				// 向上转换是安全的
	cout << typeid(p_b).name() << endl;				// B*
	cout << typeid(p_b_a).name() << endl;			// A*,地址类型转换为A*
	cout << typeid(*p_b).name() << endl;			// B,指向的内容类型是B
	cout << typeid(*p_b_a).name() << endl;			// A,指向的内容类型是A

	auto p_b_a_b = static_cast<B*>(p_b_a);			// 编译OK,向下转换不安全,建议使用dynamic_cast
	if (p_b_a_b)
	{
		cout << typeid(p_b_a_b).name() << endl;		// B,指向的内容类型是B
		cout << typeid(*p_b_a_b).name() << endl;	// A,指向的内容类型是A
		p_b_a_b->f();
		p_b_a_b->g();								// 这里可以调用成功,因为本来p_b_a_b就是p_b变量的内存数据布局
	}
	else
	{
		cout << "p_b_a_b is nullptr" << endl;
	}

	auto p_a_b = static_cast<B*>(p_a);				// 编译OK,向下转换不安全,建议使用dynamic_cast
	if (p_a_b)
	{
		cout << typeid(p_a_b).name() << endl;		// B,指向的内容类型是B
		cout << typeid(*p_a_b).name() << endl;		// A,指向的内容类型是A
		p_a_b->f();
		//p_a_b->g();								// 执行这里就会崩溃
	}
	else
	{
		cout << "p_a_b is nullptr" << endl;
	}

	delete p_a;
	p_a = nullptr;
	delete p_b;
	p_b = nullptr;

	std::cout << std::endl;
}

/*
	dynamic_cast<type-name>(expression):主要用于多态类之间的类型转换,类层次间的上行转换和下行转换。
	动态类型转换,运行时检查类型安全(转换失败返回NULL),是一种RTTI(Run-Time Type Identification)-运行时类型识别机制:
	1)dynamic_cast是运行时处理的,运行时要进行类型检查。
	2)不能用于内置的基本数据类型的强制转换。
	3)dynamic_cast转换的目标类型必须是指针或引用。dynamic_cast转换如果成功的话返回的是指向类的指针或引用,指针转换失败的话则会返回nullptr;
	因为不存在所谓空引用,所以引用类型的dynamic_cast转换与指针类型不同,在引用转换失败时,会抛出std::bad_cast异常,该异常定义在头文件typeinfo中。
	4)在使用dynamic_cast转换时,在类层次间进行上行转换(子类指针指向父类指针)时,dynamic_cast和static_cast 的效果是一样的。
	在进行下行转换(父类指针转化为子类指针)时,dynamic_cast具有类型检查的功能,比static_cast 更安全。向下转换的成功与否还与将要转换的类型有关,
	即要转换的指针指向的对象的实际类型与转换以后的对象类型一定要相同,否则转换失败。在C++中,编译期的类型转换有可能会在运行时出现错误,
	特别是涉及到类对象的指针或引用操作时,更容易产生错误。Dynamic_cast操作符则可以在运行期对可能产生问题的类型转换进行测试。
	5)使用dynamic_cast进行转换的,基类中一定要有虚函数,否则编译不通过;向下转换时,在编译时就会校验包含虚函数的继承关系。
*/
void Test_Dynamic_Cast()
{
	std::cout << __FUNCTION__ << ":" << std::endl;

	//1. 正常类的基类A*指针类型p_a指向派生类B的对象p_b
	//	typeid(p_a)!=typeid(p_b),typeid(p_a)是A*,typeid(p_b)是B*类型
	class A{};
	class B : public A {};
	
	std::cout << "A-B reference:" << std::endl;
	A a;
	B b;
	auto& b_a = dynamic_cast<A&>(b);				// 向上转换是安全的,类似static_cast
	cout << typeid(b).name() << endl;				// B
	cout << typeid(b_a).name() << endl;				// A
	//auto& b_a_b = dynamic_cast<B&>(b_a);			// 编译报错,因为b_a此时已经是A类型了,不能向下进行非多态类型转换
	//auto& a_b = dynamic_cast<B&>(a);				// 编译报错,不能向下进行非多态类型转换

	std::cout << "A-B pointer:" << std::endl;
	A* p_a = new A;
	B* p_b = new B;
	auto p_b_a = dynamic_cast<A*>(p_b);				// 向上转换是安全的,类似static_cast
	cout << typeid(p_b).name() << endl;				// B*
	cout << typeid(p_b_a).name() << endl;			// A*,地址类型转换为A*
	cout << typeid(*p_b).name() << endl;			// B,指向的内容类型是B
	cout << typeid(*p_b_a).name() << endl;			// A,指向的内容类型是A
	//auto p_b_a_b = dynamic_cast<B*>(p_b_a);		// 编译报错,因为p_b_a此时已经是A*类型了,不能向下进行非多态类型转换
	//auto p_a_b = dynamic_cast<B*>(p_a);			// 编译报错,不能向下进行非多态类型转换

	delete p_a;
	p_a = nullptr;
	delete p_b;
	p_b = nullptr;

	//2. 包含虚函数基类C*指针类型p_c指向派生类D的对象p_d
	// typeid(p_c)!=typeid(p_d),typeid(p_c)和typeid(p_d)是A*
	class C{ public: virtual void func(){} };
	class D : public C{};
	class E : public C{};
	std::cout << "C-D reference:" << std::endl;
	C c;
	D d;
	E e;
	auto& d_c = dynamic_cast<C&>(d);				// 向上转换是安全的,类似static_cast
	cout << typeid(d).name() << endl;				// D
	cout << typeid(d_c).name() << endl;				// D, 因为是引用类型,只是定义一个别名,所以d_c还是D类型

	auto& d_c_d = dynamic_cast<D&>(d_c);			// OK,因为d_c也是D类型,所以运行时检查也是OK的
	cout << typeid(d_c_d).name() << endl;			// D

	try
	{
		auto& c_d = dynamic_cast<D&>(c);			// 向下转换,编译OK,但运行时报错,不能将基类转换为子类,运行时校验不安全
		auto& e_d = dynamic_cast<D&>(e);			// 向下转换,编译OK,但运行时报错,相同基类不同子类之间的交叉转换,运行时校验不安全
	}
	catch (bad_cast &ex){
		cout << ex.what() << endl;					// 引用转换失败时,会抛出std::bad_cast异常
	}

	std::cout << "C-D pointer:" << std::endl;
	C* p_c = new C;
	D* p_d = new D;
	E* p_e = new E;
	C* p_d_c = dynamic_cast<C*>(p_d);				// 向上转换是安全的,类似static_cast
	cout << typeid(p_d).name() << endl;				// D*
	cout << typeid(p_d_c).name() << endl;			// C*,地址类型转换为C*
	cout << typeid(*p_d).name() << endl;			// D,指向的内容类型还是一直是D
	cout << typeid(*p_d_c).name() << endl;			// D,指向的内容类型还是一直是D

	D* p_d_c_d = dynamic_cast<D*>(p_d_c);			// OK
	cout << typeid(p_d_c_d).name() << endl;			// D*,地址类型转换为D*
	cout << typeid(*p_d_c_d).name() << endl;		// D,指向的内容类型还是一直是D
	
	D* p_c_d = dynamic_cast<D*>(p_c);				// 向下转换,编译OK,但运行时报错,不能将基类转换为子类,运行时校验不安全
	//p_c_d->func();								// 这里会运行时崩溃
	if (p_c_d)
	{
		cout << typeid(p_c_d).name() << endl;
		cout << typeid(*p_c_d).name() << endl;
	}
		
	else
		cout << "p_c_d is nullptr." << endl;		// 转换失败,返回nullptr

	D* p_e_d = dynamic_cast<D*>(p_e);				// 向下转换,编译OK,但运行时报错,相同基类不同子类之间的交叉转换,运行时校验不安全
	//p_e_d->func();								// 这里会运行时崩溃
	if (p_e_d)
	{
		cout << typeid(p_e_d).name() << endl;
		cout << typeid(*p_e_d).name() << endl;
	}
	else
		cout << "p_e_d is nullptr." << endl;		// 转换失败,返回nullptr

	delete p_c;
	p_c = nullptr;
	delete p_d;
	p_d = nullptr;
	delete p_e;
	p_e = nullptr;

	std::cout << std::endl;
}

/*
	reinterpret_cast<type-name>(expression)
	不同类型的指针类型转换用reinterpret_cast。
	1)转换的类型必须是一个指针,应用、算术类型、函数指针或者成员指针。
	2)在比特级别上进行转换,可以把一个指针转换成一个整数,也可以把一个整数转换成一个指针(先把一个指针转换成一个整数,
	在把该整数转换成原类型的指针,还可以得到原先的指针值)。但不能将非32bit的实例转成指针。
	3)最普通的用途就是在函数指针类型之间进行转换。
	4)reinterpret_cast意图执行低级转型,实际动作(及结果)可能取决于编辑器,这也就表示它不可移植。
*/
void Test_Reinterpret_Cast()
{
	std::cout << __FUNCTION__ << ":" << std::endl;

	auto func_do = []()->int{return 0; };

	typedef void(*func_ptr)();		//FuncPtr is 一个指向函数的指针,该函数没有参数,返回值类型为void
	func_ptr func_ptr_arr[10];		//10个FuncPtrs指针的数组

	//func_ptr_arr[0] = &func_do;	// 编译错误,类型不匹配
	func_ptr_arr[0] = reinterpret_cast<func_ptr>(&func_do);		//不同函数指针类型之间进行转换

	cout << typeid(func_do).name() << endl;
	cout << typeid(func_ptr_arr[0]).name() << endl;

	std::cout << std::endl;
}

int main()
{
	Test_Const_Cast();
	Test_Static_Cast();
	Test_Dynamic_Cast();
	Test_Reinterpret_Cast();

	while (getchar() != ' ');
	return 0;
}

输出效果图如下:

简要总结:

去const属性用const_cast或volatile属性

基本类型转换用static_cast,类似C风格中的强制转换

多态类之间的类型转换用dynamic_cast

不同类型的指针类型转换用reinterpret_cast

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值