详解c++---类型转换

目录标题

  • c语言的类型转换
  • static_cast
  • reinterpret_cast
  • const_cast
  • dynamic_cast

c语言的类型转换

在C语言中,如果赋值运算符左右两侧类型不同,或者形参与实参类型不匹配,或者返回值类型与接收返回值类型不一致时,就需要发生类型转化,C语言中总共有两种形式的类型转换:隐式类型转换和显式类型转换。当两个数据类型相关联的时候就可以使用隐式类型转换,比如说int char double long 之间就可以使用隐式类型转换,比如说下面的代码:

int main()
{
	int i = 10;
	double d = i;
	printf("%d  %.2f", i, d);
	return 0;
}

运行的结果如下:
详解c++---类型转换_第1张图片
但是如果两个数据的关联性不强或者完全不相关的话就不能使用隐式类型转换而得使用强制类型转换,比如说下面的代码:double和int*就是两个毫不相关的类型,这个时候使用隐式类型转换的时候就会报错,比如说下面的代码:

int main()
{
	int i = 10;
	int* pi = &i;
	int d = pi;
	return 0;
}

详解c++---类型转换_第2张图片

但是使用强制类型转换这里就不会报错,比如说下面的代码:

int main()
{
	int i = 10;
	int* pi = &i;
	int d = (int)pi;
	printf("%d  %p", d, pi);
	return 0;
}

详解c++---类型转换_第3张图片
虽然c语言又两种转换方式,但是转换的可视性比较差,所有的转换形式都是以一种相同形式书写,难以跟踪错误的转换,比如说有些地方会自动地进行整型提升,有些地方又会发生截断,而且这些操作都是它自己偷偷在底下搞得,所以有时候就会造成一些错误,那么为了解决这个问题c++就提供了4种类型得转换来解决c原因的问题,但是因为C++要兼容C语言,所以C++中还可以使用C语言的转化风格。

static_cast

static_cast使用相似类型的转换,比如说int和char就属于相似的类型,int和double也属于相似的类型,那么对于这些类型的转换我们就可以使用static_cast来进行转换,使用的方式也很简单尖括号里面写入你要转换的类型,括号里面填入你想要转换的对象,最后赋值给对应类型的对象,比如说下面的代码:

int main()
{
  double d = 12.34;
  int a = static_cast<int>(d);
  cout<<a<<endl;
  return 0;
}

这里就是将变量d转换成为int类型并赋值int类型的变量a。

reinterpret_cast

reinterpret_cast就使用于不相关类型之间的转换,比如说int和int*就属于不相关的类型,那么这里的使用方法就跟上面的static_cast是一样的比如说下面的代码:

int main()
{
 double d = 12.34;
 int a = static_cast<int>(d);
 cout << a << endl;
 // 这里使用static_cast会报错,应该使用reinterpret_cast
 //int *p = static_cast(a);
 int *p = reinterpret_cast<int*>(a);
 return 0;
}

const_cast

这个转换就是将原本的const属性去掉,也就是可以将const变量修改成为非const变量,比如说下面的代码:

int main()
{
	const int i = 10;
	int* p = const_cast<int*>(&i);
	*p = 20;
	cout << i << endl;
	cout << *p << endl;
	return 0;
}

&i得到的类型原本是const int *但是经过const_cast修改之后就变成了int *所以就可以正常的赋值,但是将上面的代码运行一下就会发现这样的结果:
详解c++---类型转换_第4张图片
那这是为什么呢?指针p指向的是变量i,可是为什么这里打印出来的结果不一样呢?我们再通过调试来查看一下
在这里插入图片描述
可以看到调试看到的内容和打印出来的内容竟然不一样,那这是为什么呢?原因很简单因为默认情况下编译器认为const变量修饰的变量不会被修改所以就会放到寄存器里面,这样在用这个变量的时候就直接会去寄存器中获取这个变量,然后修改变量i的时候会对内存里面的内容进行修改,但是监视窗口却是从内存里面访问数据不是从寄存器里面获取,所以就看到不同的样子,那么要想解决这个问题就得在变量前面加一个volatile关键字这样就不会去寄存器上取,而是从内存上取。那么这里的代码就如下:

int main()
{
	volatile const int i = 10;
	int* p = const_cast<int*>(&i);
	*p = 20;
	cout << i << endl;
	cout << *p << endl;
	return 0;
}

运行的结果如下:
详解c++---类型转换_第5张图片
const_cast 是去掉const属性,将它单独分为一种,就是在警示你这个很危险用的时候要小心一点。

dynamic_cast

dynamic_cast用于将一个父类对象的指针/引用转换为子类对象的指针或引用(动态转换)
向上转型:子类对象指针/引用->父类指针/引用(不需要转换,赋值兼容规则)
向下转型:父类对象指针/引用->子类指针/引用(用dynamic_cast转型是安全的)
那这里为什么要说使用用dynamic_cast转型是安全的呢?原因很简单因为子类可以转换成为父类,父类也可以转换成为子类,但是这里可能会出现问题,父类的变成子类之后可能会对指向的空间照成非法访问,所以就有了dynamic_cast这个会检查转换,如果是父类转换成为子类的话会阻止转换使其失败,比如说下面的代码:

class A
{
public:
	virtual void f() {}

	int _a = 0;
};

class B : public A
{
public:
	int _b = 0;
};
void Func(A* ptr)
{
	// 直接转换是不安全的
	B* bptr = (B*)ptr;
	cout << bptr << endl;
}
int main()
{
	A aa;
	B bb;
	Func(&aa);
	Func(&bb);
	return 0;
}

func函数是用父类来进行接收,但是我们可以将父类的类型转换成为子类的类型并且还不会报错,比如说下面的代码:
详解c++---类型转换_第6张图片
那么这个时候可能就会产生一个问题,子类的对象中既有父类的内容又有子类的内容,如果类型为子类指向的却是父类的内容的话,我们是不是就会在无意间发生越界访问呢?比如说下面的代码:

void Func(A* ptr)
{
	// 直接转换是不安全的
	B* bptr = (B*)ptr;
	cout << bptr << endl;
	bptr->_a++;
	bptr->_b++;
	cout << bptr->_a << endl;
	cout << bptr->_b << endl;
}

详解c++---类型转换_第7张图片
可以看到这里出现了问题,所以如果ptr指向的是子类对象,那么转回子类类型就没有问题,如果ptr指向的是父类对象,那么转换成为子类对象就可能会出现越界访问的风险,所以这个时候就有了dynamic_cast, dynamic_cast只能用于父类含有虚函数的类(记住父类必须得有虚函数),并且dynamic_cast会先检查是否能转换成功,能成功则转换不能则返回0,所以我们可以添加一个if来进行判断,如果bptr的值不为空则表示转换成功,如果为空的话就表示转换失败,就不让它执行里面的语句,那么这里的代码就如下:

void Func(A* ptr)
{
	// 直接转换是不安全的
	B* bptr = dynamic_cast<B*>(ptr);
	cout << bptr << endl;
	if (bptr)
	{
		bptr->_a++;
		bptr->_b++;
		cout << bptr->_a << endl;
		cout << bptr->_b << endl;
	}
}

代码的运行结果如下:
详解c++---类型转换_第8张图片

你可能感兴趣的:(c++详解,c++,开发语言)