C++进阶------C++的类型转换

作者前言

🎂 ✨✨✨✨✨✨🍧🍧🍧🍧🍧🍧🍧🎂

​🎂 作者介绍: 🎂🎂

🎂 🎉🎉🎉🎉🎉🎉🎉 🎂

🎂作者id:老秦包你会, 🎂

简单介绍:🎂🎂🎂🎂🎂🎂🎂🎂🎂🎂🎂🎂🎂🎂🎂

喜欢学习C语言、C++和python等编程语言,是一位爱分享的博主,有兴趣的小可爱可以来互讨 🎂🎂🎂🎂🎂🎂🎂🎂

🎂个人主页::小小页面🎂

🎂gitee页面:秦大大🎂

🎂🎂🎂🎂🎂🎂🎂🎂

🎂 一个爱分享的小博主 欢迎小可爱们前来借鉴🎂


类型转换

C++和C的类型转换

前面我们学习c++的时候知道,如果赋值运算符左右两侧类型不同,或者形参与实参类型不匹配,或者返回值类型与接收返回值类型不一致时,就需要发生类型转化,C语言中总共有两种形式的类型转换:隐式类型转换显式类型转换
隐式类型转换简单来说就是编译器会自动进行转换隐式类型转化:编译器在编译阶段自动进行,能转就转,不能转就编译失败,如下:

复制代码
//隐式类型转换
	int i = 10;
	double y = i;
	printf("i: %d, y: %.3f", i, y);

显式类型转化:需要用户自己处理,如下:

复制代码
//显示类型转换
	int* ptr = &i;
	int x = (int)ptr;
	printf("ptr: %p, x: %d", ptr, x);

我们知道,类型转换都会产生临时变量,临时变量不仅仅由类型转换而来,比如一个表达式a+b,这个是会产生临时变量。

回归原题,c++的类型转换是需要一些联系的,有一定类型的关联,

C关联如下:

复制代码
整形和浮点型
指针和整形
bool和整形
bool和指针
指针类型之间(比如int*和double*)

C++的关联:

c++比C多出了自定义类型和内置类型的转换,举例子如下:

复制代码
std::string str = "wwkkw";

这里会产生一个临时对象,然后再拷贝构造给str,本质就是借助构造来实现

自定义类型和自定义类型 的转换,就拿 initializer_list和容器为例子,以vector为例子:

复制代码
std::vector<int> ve = { 1,2,3,4,5,6 };

这行代码执行的就是上面图的构造,或者使用显示构造更明显

复制代码
// 显式使用initializer_list
    std::initializer_list<int> il = {10, 20, 30};
    std::vector<int> vec3(il);  // 直接构造

本质也是借助构造函数来实现的,如果是自定义类型转换成内置类型就不能这样了

自定义类型->成内置类型,我们以自定义类型转换成整形为例子:

复制代码
class A
{
public:
	operator int()
	{
		return _a1 + _a2;
	}
private:
	int _a1 = 1;
	int _a2 = 3;
};
int main()
{
	A aa;
	int b = (int)aa;//显示转换
	int c = aa;//隐式转换

	return 0;
}

这里需要借助operator来进行实现,这里比较特殊, 可以理解和前置++和后置++一样的特殊。

除此之外,还有一些情况,比如无符号和有符号之间的隐式转换

复制代码
void Insert(size_t pos, int x)
{
//size_t end = 10;
	int end = 10;
	while (end >= pos)
	{
		cout << end << "移开" << endl;
		end--;
	}
}
int main()
{
	Insert(0, 1);

	return 0;
}

因为计算机存储的都是补码,所以当使用size_t的时候,当end为0时再减1,就会-1,-1的补码就是1111.11111.1111.1111.1111.1111.1111.1111,因为size_t是无符号的,第一位不为有符号,所以大小就是4294967295,进行无限循环

如果使用的是int,当end为-1时,因为时int,有符号的,所以补码转换为原码就是-1.

为啥会无限循环,因为这里会进行隐式类型转换。一般时范围小的向范围大的转,啥是范围大小,

C++的四种强制类型转换

标准C++为了加强类型转换的可视性, 引入了四种命名的强制类型转换操作符:
static_castreinterpret_castconst_castdynamic_cast

static_cast(静态转换)

这个对应的就是上面讲的隐式类型转换

复制代码
int main()
{
	double a = 1.2;
	//可观性的隐式类型转换,强烈使用
	int b = static_cast<int>(a);
	//隐式类型转换
	int c = a;
	return 0;
}

reinterpret_cast (强制类型转换)

对应的就是强制类型转换也就是显示类型转换,,需要注意的就是能进行隐式类型转换的不能使用这个。

复制代码
int main()
{
	double a = 1.20;
	int b = (int)a;
	//int c = reinterpret_cast<int>(a);//能进行隐式类型转换的不能使用
	int* c = reinterpret_cast<int*>(&a);
	return 0;
}

const_cast(强制类型转换去const)

const_cast最常用的用途就是删除变量的const属性,方便赋值。

坑1:

复制代码
int main()
{
	const int a = 10;//a的地址类型是 const int *,表示值不能被修改
	
	int* b = const_cast<int*>(&a);
	*b = 20;
	cout << "a;" << a << endl;
	cout << "b;" << *b << endl;
	return 0;
}

产生这个结果 的原因就是,编译器的优化,编译器认为const int a = 10;不会被改变,就放到寄存器中,使用的话就从寄存器中取,图中的监视窗口获取的数据是拿内存的,在内存中,a 和b已经改了,但是在寄存器中的a没有改,打印的结果就会不一样。这个结果和使用强制类型转换的结果一样 (int b = (int* &a))。为了避免编译器把a往寄存器扔,可以添加关键字volatile,这也就是const_cast和reinterpret_cast 的主要区别,使用const_cast可以让我们有一个预防心理,是否添加关键字。

复制代码
volatile const int a = 10;//a的地址类型是 const int *,表示值不能被修改

dynamic_cast

dynamic_cast用于将一个父类对象的指针/引用转换为子类对象的指针或引用(动态转换)

向上转型:子类对象指针/引用->父类指针/引用(不需要转换,赋值兼容规则)

如下:

复制代码
class A
{
public:
	A(const int& a , const int& b)
		:_a(a)
		,_b(b)
	{
	}
	virtual void funtion() 
	{
	}
private:
	int _a;
	int _b;
};
class B : public A
{
public:
	B(const int& a , const int& b)
		:A(a, b)	
	{
	}
};
int main()
{

	B bb(10,20);
	A aa = bb;
	A* a = &bb;
	return 0;
}

这个规则俗称切片,这个过程中,这个赋值过程不会产生临时变量,也就不存在类型转换

向下转型 :父类对象指针/引用->子类指针/引用(用dynamic_cast转型是安全的)。

到这里就会有一些问题,为啥不能写一个函数进行向下转换,原因如下:

为啥不能写一个函数进行相关的基类转子类的函数,如下:

复制代码
class A
{
public:
	A(const int& a)
		:_a(a)
	{

	}
	virtual void funtion()
	{

	}
	int _a;
	
};
class B : public A
{
public:
	B(const int& a , const int& b)
		:A(a)
		,_b(b)
		
	{

	}
	int _b;

};
void fun(A* pa)
{
	B* pb = (B*)pa;
	cout << pb->_a << endl;
	cout << pb->_b << endl;
}

这样的写法只是适合子类,不适合基类,如果传入的地址是基类的,如图:

可以看出,类型转换为子类类型的就会在进行访问的时候,访问的内容就是错误的,也就是内存越界。这样是不安全的。

如果使用的dynamic_cast,可以避免这个问题

使用这个dynamic_cast有如下注意:

  1. dynamic_cast只能用于父类含有虚函数的类

    1. dynamic_cast会先检查是否能转换成功,能成功则转换,不能则返回0

    void fun(A* pa)
    {
    //B* pb = (B*)pa;
    B* pb = dynamic_cast<B*>(pa);
    if (pb != nullptr)
    {
    cout << pb->_a << endl;
    cout << pb->_b << endl;
    }
    else
    {
    cout << "转换失败" << endl;
    }

    }

效果如下:

相关推荐
星辰烈龙2 小时前
黑马程序员JavaSE基础加强d2
java·开发语言
superman超哥2 小时前
仓颉性能瓶颈定位方法深度解析
c语言·开发语言·c++·python·仓颉
ps酷教程2 小时前
HttpObjectDecoder源码浅析
java·netty·httpaggregator
是苏浙2 小时前
零基础入门Java之认识String类
java·开发语言
leaves falling2 小时前
c语言-static和extern
c语言·开发语言
武汉唯众智创2 小时前
“物联网 Python 开发教程”课程教学解决方案
开发语言·python·物联网·物联网技术·物联网 python 开发·python 开发
悟空码字2 小时前
从零到一搭建SpringCloud微服务,一场代码世界的“分家”大戏
java·后端·spring cloud
时光Autistic2 小时前
【搭建教程】腾讯混元3D模型部署
开发语言·python·3d·github
于樱花森上飞舞2 小时前
【多线程】常见的锁策略与锁
java·开发语言·算法·java-ee