c++类型转换

1 静态转换(static_cast)

static_cast 全部用于明确定义达到转换,包括编译器允许我们所做的不用强制转换的安全变换和不太安全但清楚定义的变换。

1.1 用于类层次结构中基类(父类)和派生类(子类)之间指针或引用的转换

1.进行上行转换(把派生类的指针或引用转换成基类表示)是安全的
2.进行下行转换(把基类指针或引用转换成派生类表示)时,由于没有动态类型检查,所以是不安全的。这里的不安全是指进行向下转换时,转换后所得的子类指针是一个非空值,这就可能造成原本一个基类指针在静态转换成派生类指针后可以有由这个指针去访问原来基类中不存在的派生类成员(而动态转换在这种情况下会返回一个空的指针)

class Animal{};
class Dog : public Animal{};

// 继承关系指针互相转换
void test02(){
	// 继承关系指针转换
	Animal* animal01 = NULL;
	Dog* dog01 = NULL;
	// 子类指针转成父类指针,安全
	Animal* animal02 = static_cast<Animal*>(dog01);
	// 父类指针转成子类指针,不安全
	Dog* dog02 = static_cast<Dog*>(animal01);
}

// 继承关系引用相互转换
void test03(){

	Animal ani_ref;
	Dog dog_ref;
	// 继承关系引用转换
	Animal& animal01 = ani_ref;
	Dog& dog01 = dog_ref;
	// 子类引用转成父类引用,安全
	Animal& animal02 = static_cast<Animal&>(dog01);
	// 父类引用转成子类引用,不安全
	Dog& dog02 = static_cast<Dog&>(animal01);
}

1.2 用于基本数据类型之间的转换

如把int转换成char,把char转换成int。这种转换的安全性也要开发人员来保证

//基础数据类型转换
void test01(){
	char a = 'a';
	double b = static_cast<double>(a);
}

1.3 无继承关系指针或引用转换不能转换

class Animal{};
class Other{};

// 无继承关系指针转换
void test04(){
	Animal* animal01 = NULL;
	Other* other01 = NULL;
	// 转换失败,语法上不允许
	Animal* animal02 = static_cast<Animal*>(other01); // error
}

2 动态转换(dynamic_cast)

针对继承体系中,主要用于类型安全的向下转化。在类层次间进行上行转换时,dynamic_cast和static_cast的效果是一样的,是安全的。在进行下行转换时,dynamic_cast具有类型检查的功能,比static_cast更安全。

2.1 上行转换

class Animal {
public:
	virtual void ShowName() = 0;
};
class Dog : public Animal{
	virtual void ShowName(){
		cout << "I am a dog!" << endl;
	}
};
// 继承关系指针
void test02(){

	Animal* animal01 = NULL;
	Dog* dog01 = new Dog;

	// 子类指针转换成父类指针
	Animal* animal02 = dynamic_cast<Animal*>(dog01);
	animal02->ShowName();
}

// 继承关系引用
void test03()
{
	Dog dog_ref;
	Dog& dog01 = dog_ref;
	// 子类引用转换成父类引用
	Animal& animal02 = dynamic_cast<Animal&>(dog01);
	animal02.ShowName();
}


}

2.2 下行转换

#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
#include <string>
using namespace std;
class Base
{
public:
	virtual void Show()
	{
		cout << "Base show" << endl;
	}
};

class D :public Base
{
public:
	void Print()
	{
		cout << "D Print" << endl;
	}
public:
	int x;
};

void fun(Base* pb)
{
	// 若传进来的是子类指针,这样由子类指针转为基类指针再转为子类指针是安全的
	// 但传进来的是父类指针,这样转换后pd得到的是空指针,程序可以做一个判空的操作来保证安全性,
	// 但是这里有一点比较诡异,如果传进来的是基类指针,,没有判空分支的情况下
	// 即使转换后的pd为空,在用这个指针访问子类独有的函数Print时,
	// 程序竟然是正常运行的,只有在访问子类独有的成员变量时,程序才崩溃掉

	// D* pd = (D*)pb;
	D* pd = dynamic_cast<D*>(pb); 
	//pd->x = 10;
	//pd->Print();
	if (pd != NULL)
	{
		pd->x = 10;
		pd->Print();
	}
	else
	{
		cout << "can not transform" << endl;
	}
}
int main()
{
	Base b;
	//D d;
	fun(&b);

	system("pause");
	return 0;
}

2.3 动态转换的原因

可能有人会质疑,为什么不直接用子类的指针指向其对象? 然后想怎么访问怎么访问,干嘛要先转成基类指针,又转回来?或者又有问题 ,就是pb是基类指针指向子类对象时,我要访问子类对象,再强转回来就完事了,为什么要用dynamic_cast? 这两个问题有一个共同的答案就是:

在C++中存在虚函数,也就存在了多态性,对于多态性的对象,在程序编译时可能会出现无法确定对象的类型的情况。当类中含有虚函数时,其基类的指针就可以指向任何派生类的对象,这时就有可能不知道基类指针到底指向的是哪个对象的情况,类型的确定要在运行时利用运行时类型标识做出。这时为了获得一个对象的类型就可以使用typeid函数;要转换类型就可以用dynamic_cast操作符。因为dynamic_cast与运行时类型转换密切相关,也就是它会在程序运行时进行类型转换。这样当我们不知道基类指针指向哪个子类对象时,我们只需要dynamic_cast直接转化为我们想要的类型即可。

2.4 dynamic_cast 注意点

  1. dynamic_cast转换符将一个指向派生类的基类指针或引用转换为派生类的指针或引用,但需注意dynamic_cast转换符只能用于含有虚函数的类。这是它的作用,也是其特点。
  2. dynamic_cast与运行时类型转换密切相关,所以在无法确定基类指针指向那个子类对象时,可以用它,并且不要忘了用if语句判断是否强转成功,成功返回强转后的地址,失败返回0。
  3. 在使用dynamic_cast将基类指针强转为子类指针时,若要被强转的基类指针原本指向子类的对象,则是可以强转成功的;若要被强转的基类指针原本指向基类的对象,则是不可以强转成功的, 若把基类的对象的地址手动强转成子类指针后, 再用dynamic_cast强转成子类指针,表面是可以转换成功,但其实dynamic_cast没做什么事,转换后的指针还是不能访问子类的独有成员。
  4. 一般情况下不推荐这样使用dynamic_cast转换符,因为dynamic_cast的转换并不会总是成功的。

2.5 普通类型、无继承关系指针或引用不能转换

//普通类型转换 语法上不允许
void test01()
{
	// 不支持基础数据类型
	int a = 10;
	double a = dynamic_cast<double>(a); // error
}

// 无继承关系指针或引用转换 语法上不允许
class Animal{};
class other{};

void test04()
{
	Animal* animal01 = NULL;
	Other* other = NULL;
	Animal* animal02 = dynamic_cast<Animal*>(other); // error
}

3 常量转换(const_cast)

使用const_cast 用来修改类型的const属性,常量指针被转化成非常量指针,并且仍然指向原来的对象,常量引用被转换成非常量引用,并且仍然指向原来的对象,注意:不能直接对非指针和非引用的变量使用const_cast操作符去直接移除它的const,使用const_cast去除const限定的目的不是为了修改它的内容,使用const_cast去除const限定,通常是为了函数能够接受这个实际参数

3.1 非常量指针转或引用换成常量指针或引用


void test01()
{
	// 非常量指针转换成常量指针
	int* pp = NULL;
	const int* npp = const_cast<const int*>(pp);

	// 非常量引用转换成常量引用
	int num = 10;
	int & refNum = num;
	const int& refNum2 = const_cast<const int&>(refNum);

	 // 不能对非指针或非引用进行转换
	const int a = 10; 
	int b = const_cast<int>(a);  // error
}

3.2 常量指针转或引用换成非常量指针或引用

#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
#include <string>
using namespace std;
int main()
{
	const int a = 10;
	const int *p = &a;
	int *q = const_cast<int*>(p);
	*q = 100;
	cout << "a = "<< a << endl;
	cout << "*q = " << *q << endl;

	cout << "==============================" << endl;

	int &ref = const_cast<int&>(a);
	ref = 200;
	if (a == ref)
		cout << "yes" << endl;
	else
		cout << "no" << endl;

	cout << "a= " << a << endl;
	cout << "ref = " << ref << endl;

	system("pause");
	return 0;
}

在这里插入图片描述
也许大家都有过这样的疑惑:const_cast可以去除一个常量的const属性,去除const属性后应该可以对“常量”进行修改,通过调试器发现内存中的值是被改变的,可是再传递这个“常量”的时候,值却一直保持原状,实在古怪
在这里插入图片描述
监视器中可以看到a的值在常量属性去掉后,在监视器中的值是被改变了的,但是程序中的判等分支没有进去,打印出的值还是原来的10,其实加const只是告诉编译器不能修改而不是真正地不可修改,如果程序员不注意而去修改了它会报错,现在我们利用const_cast去除了常量性,然后通过指针和引用对其进行了修改,所以通过指针打印或者引用传参的时候就能看出其内存确实变化了,但为了保护val这个变量本来的const特性,所以每次我们使用val时,系统都将其替换成初始值10,确保了a还是“不可变”的。***记住,只有当const限定符加在已经初始化的全局变量前面的时候,此时变量处在.rodata段(linux下),才是真正的不可修改(若要强制修改,程序会异常终止)***,否则通过指针都是可以修改的,虽然在使用时系统都将其替换成初始值
在这里插入图片描述
可以看到上图,去掉一个全局变量的const属性时,当用引用改变它的值时,程序会异常终止。

4 重解释转换(reinterpret_cast)

这是最不安全的一种转化机制,最有可能出问题,reinterpret_cast把对象假想为模式,是一个完全不同类型的对象,在使用reinterpret_cast做任何事之前,为了安全,实际上总是需要reinterpret_cast转换回原来的类型
C++中的reinterpret_cast主要是将数据从一种类型的转换为另一种类型。所谓“通常为操作数的位模式提供较低层的重新解释”,也就是说将数据以二进制存在形式的重新解释。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值