跳至主要內容

强制转换

张威大约 7 分钟c/c++c++基础

强制转换

类型转换有c风格的,当然还有c++风格的。c风格的转换的格式很简单

TYPE a = (TYPE)EXPRESSION;

但是c风格的类型转换有不少的缺点,有的时候用c风格的转换是不合适的,因为它可以在任意类型之间转换,比如你可以把一个指向const对象的指针转换成指向非const对象的指针,把一个指向基类对象的指针转换成指向一个派生类对象的指针,这两种转换之间的差别是巨大的,但是传统的c语言风格的类型转换没有区分这些。 另一个缺点就是,c风格的转换不容易查找,它由一个括号加上一个标识符组成,而这样的东西在c++程 序里一大堆。 c++为了克服这些缺点,引进了4个新的类型转换操作符(非函数),他们是static_cast,const_cast,dynamic_cast,reinterpret_cast

static_cast

的类型转换,最常用的类型转换符,在正常状况下的类型转换, 用于将一种基本数据类型转换成另一种基本数据类型,如把int转换为float

int iNumber = 100
float fNumber = 0
fNumber = (float) iNumber;//C风格
fNumber = static_cast<float>(iNumber);

也可以完成指针之间的转换,例如可以将void*指针转换成其他类型的指针

void *pVoid = malloc(sizeof(int));
int *pInt = static_cast<int*>(pVoid);
*pInt = 1;

int iNumber = 1;
int *pInt = &iNumber;
float *pFloat = static_cast<float *>(pInt);//error

总结,static_cast的用法主要有以下几种:

  1. 用于基本数据类型之间的转换,如把int转成char,把int转成enum。这种转换的安全性也要开发人员来保证
  2. void指针转换成目标类型的指针,但
  3. 任何类型的表达式转换成void类型
  4. 用于类层次结构中基类和派生类之间指针或引用的转换。进行上行转换(把;进行下行转换(把

const_cast

该运算符常量指针转化成非常量指针,并且仍然指向原来的对象;常量引用被转换成非常量引用,并且仍然指向原来的对象;常量对象被转换成非常量对象。

const int number = 100;
int *pInt = &number;	//error
int *pInt1 = (int*)&number;	//C中类型转换
int *pInt2 = const_cast<int *>(&number);	//C++中类型转换const_cast

注意:

  1. 不考虑const或valatile,才可以进行合理的类型转换。

转换为相同类型的时候,我们通过反汇编open in new window查看时候,发现**所生成的的**。

但是在转换成汇编指令之前,即编译阶段,就有所不同

注意1:不考虑const或valatile后,类型保持一致才可以进行合理的类型转换

const int a = 10;
char *p1 = (char*)&a;//C中类型转换,可以
char *p2 = const_cast<char*>(&a);//C++中类型转换const_cast,不可以
在这里插入图片描述
在这里插入图片描述

如果通过C中类型转换可以将int * 转换为多种不同的类型,没有任何问题;这里为整型常量的地址,但是如果通过const_cast将整型常量地址转换为另一个指针类型不匹配的指针,是不可以的。。防止了C中低级别的类型强转任意的转换指针的类型导致一些不确定的错误。

注意2:const_cast<里面必须是指针或引用类型>,否则出错,例如:

const int a = 10;
int b = const_cast<int>(a);

dynamic_cast

该运算符主要用于基类和派生类间的转换,尤其是向下转型的用法中。

,用于继承结构中,可以支持RTTI类型识别的上下转换及识别。将一个基类对象指针(或引用)转换到继承类指针,

class Base
{
public:
	virtual void func() = 0;
};

class Derive1 : public Base
{
public:
	void func()
	{
		cout << "call Derive1::func" << endl;
	}
};

class Derive2 : public Base
{
public:
	void func()
	{
		cout << "call Derive2::func" << endl;
	}
};

void showFunc(Base* p)
{
	p->func();//动态绑定
}

int main()
{
	Derive1 d1;
	Derive1 d2;
	showFunc(&d1);
	showFunc(&d2);

	return 0;
}

但是,我们的需求改变了。Derive实现了一个新功能的API接口函数

class Derive2 : public Base
{
public:
	void func()
	{
		cout << "call Derive2::func" << endl;
	}
	//需求更改 Derive2实现新功能的API接口函数
	void derive02func()
	{
		cout << "call derive02func()::func" << endl;
	}
};

我们的void show()应该区分判断一下,如果Base* p指向了其他的派生类对象,调用p->func()方法就好。但如果指向Derive2对象,不调用func()方法,而**** 这里就要识别*p的类型,看它指向哪个对象。此时就需要我们的dynamic_cast()了。dynamic会检查p指针是否指向的是一个Derive2类型的对象p->vfptr->vftable RTTI信息 如果是dynamic_cast,转换类型成功,返回Derive2对象地址;否则,返回nullptr。

void showFunc(Base* p)
{
	//dynamic会检查p指针是否指向的是一个Derive2类型的对象
	//p->vfptr->vftable RTTI信息 如果是dynamic_cast,
	//转换类型成功,返回Derive2对象地址;否则,返回nullptr
	Derive2 *pd2 = dynamic_cast<Derive2*>(p);
	if (pd2 != nullptr)
	{
		pd2->derive02func();
	}
	else
	{
		p->func();//动态绑定
	}
}

reinterpret_cast

类似于C风格的强制类型转换,是

该运算符可以用来处理无关类型之间的转换,即用在任意指针(或引用)类型之间的转换,以及指针与足够大的整数类型之间的转换。由此可以看出,reinterpret_cast的效果很强大,但错误的使用reinterpret_cast很容易导致程序的不安全,只有将转换后的类型值转换回到其原始类型,这样才是正确使用reinterpret_cast方式。

int *p = nullptr;
double* b = reinterpret_cast<double*>(p);

课堂代码

#include <iostream>

using std::cout;
using std::endl;

void test()
{
    int iNumber = 10;
    float fNumber = 12.345;
    /* iNumber = (int)fNumber; */
    iNumber = int(fNumber);
}

void test2()
{
    int iNumber = 10;
    float fNumber = 12.345;
    iNumber = static_cast<int>(fNumber);
    cout << "inumber = " << iNumber << endl;

    void *pret = malloc(sizeof(int));
    int *pInt = static_cast<int *>(pret);


    free(pInt);
    pInt = nullptr;
}

void test3()
{
    const int number = 100;
    /* int *p1 = &number;//error */
    int *p1 = const_cast<int *>(&number);
    cout << "*p1 = " << *p1 << endl;	//100
    printf("p1's address : %p\n", p1);//0x7ffc1abeae5c
    printf("number's address : %p\n", &number);//0x7ffc1abeae5c,地址相同(指向原对象)

    //下面这个种行为C++已经不支持了,不要这样写,了解后不要纠结这里了
    //地址相同,值不同
    cout << endl;
    *p1 = 200;//未定义行为
    cout << "*p1 = " << *p1 << endl; //200
    cout << "number = " << number << endl;//100
    printf("p1's address : %p\n", p1);	//0x7ffc1abeae5c
    printf("number's address : %p\n", &number);//0x7ffc1abeae5c
}
int main(int argc, char **argv)
{
    test3();
    return 0;
}