首页 > 编程语言 >【C++】深究类型转换

【C++】深究类型转换

时间:2024-10-31 13:15:39浏览次数:3  
标签:类型转换 const 深究 int C++ a1 cast a2

头像
⭐️个人主页:@小羊
⭐️所属专栏:C++
很荣幸您能阅读我的文章,诚请评论指点,欢迎欢迎 ~

动图描述

目录


一、类型转换

1、C语言中的类型转换

如果赋值运算符左右两侧类型不同,或者形参与实参类型不匹配,或者返回值类型与
接收返回值类型不一致时,就需要发生类型转化,转换的前提是类型之间有一定的关联。

  • 隐式类型转换:编译器自动进行,比如整形家族(int、char、unsigned int)/ 整型和浮点数
  • 强制类型转换:我们自己处理,比如整形和指针、指针之间
int main()
{
	int i = 1;
	// 隐式类型转换
	double d = i;

	int* p = &i;
	// 显示的强制类型转换
	int address = (int)p;

	return 0;
}

2、C++中的类型转换

上面举的例子都是内置类型之间,而内置类型和自定义类型之间自定义类型和自定义类型之间都是可以通过一定的方式互相转换的。

| 内置类型和自定义类型之间:

在前面的学习中我们经常说:单参数构造函数支持隐式类型转换,多参数也可以通过加{}进行隐式类型转换。

class A
{
public:
	A(int a)
		:_a1(a)
		,_a2(a)
	{}

	A(int a1, int a2)
		:_a1(a1)
		,_a2(a2)
	{}
private:
	int _a1;
	int _a2;
};

int main()
{
	string s("Are you ok?");//隐式类型转换
	A a1(1);//借助构造函数完成类型转换
	A a2({ 1, 2 });
	return 0;
}
  • C++支持内置类型隐式类型转换为类类型对象,需要有相关内置类型为参数的构造函数
  • 构造函数前加explicit就不再支持隐式类型转换(但是还可以强转)

而自定义类型转换为内置类型需要通过下面这个函数:

operator int()
{
	//...
}
  • 这个函数没有返回类型,但是有返回值
  • 函数前加explicit就不再支持隐式类型转换(但是还可以强转)
class A
{
public:
	A(int a)
		:_a1(a)
		,_a2(a)
	{}

	A(int a1, int a2)
		:_a1(a1)
		,_a2(a2)
	{}

	operator int()
	{
		return _a1 + _a2;
	}
private:
	int _a1;
	int _a2;
};

int main()
{
	string s("Are you ok?");//隐式类型转换
	A a1(1);//借助构造函数完成类型转换
	A a2({ 1, 2 });

	int x = a1;
	int y = a2;
	cout << x << endl;
	cout << y << endl;
	return 0;
}

| 自定义类型和自定义类型之间:

自定义类型之间也可以借助构造函数来完成相互转换。

class A
{
public:
	A(int a)
		:_a1(a)
		,_a2(a)
	{}

	A(int a1, int a2)
		:_a1(a1)
		,_a2(a2)
	{}

	operator int()
	{
		return _a1 + _a2;
	}

	int get() const
	{
		return _a1 + _a2;
	}
private:
	int _a1;
	int _a2;
};

class B
{
public:
	B(int b)
		:_b(b)
	{}

	B(const A& aa)
		:_b(aa.get())
	{}
private:
	int _b;
};

int main()
{
	A aa(1);
	B bb(2);
	bb = aa;//这里走了B的拷贝构造
	return 0;
}

例如:我们之前实现的list的迭代器有普通迭代器和const迭代器两种,普通迭代器用普通迭代器接收,const迭代器用const迭代器接收,而库中的list是支持普通迭代器用const迭代器接收的,那我们也可以给自己的list加上这个功能。

在这里插入图片描述

增加一个用于类型转换的构造函数:

//...
ListIterator(const ListIterator<T, T&, T*>& it)
	:_node(it._node)
{}
//...

注意:这里的参数一定是写死的,不能是const ListIterator<T, Ref, Ptr>& it


3、C语言类型转换的缺陷

  1. 转换的可视性差,所有的转换形式都是以一种相同的形式书写,难以跟踪错误的转换
  2. 隐式类型转换有些情况下可能会出现问题,比如数据精度丢失
  3. 显示类型转换将所有情况混在一起,代码不够清晰

4、C++中的四种强制类型转换

标准C++为了加强类型转换的可视性,引入了下面四种命名的强制类型转换操作符。主要是为了让类型转换有统一的规范,更加严谨。

4.1 static_cast

static_cast用于非多态类型的转换(对应隐式类型转换),编译器隐式执行的任何类型转换都可用static_cast,但它不能用于两个不相关的类型进行转换。

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

4.2 reinterpret_cast

reinterpret_cast操作符通常为操作数的位模式提供较低层次的重新解释,用于将一种类型转换为另一种不同的类型(对应强制类型转换)。

int main()
{
	double d = 3.14;
	int a = static_cast<int>(d);
	cout << a << endl;

	//这里使用static_cast会报错,应该使用reinterpret_cast
    //int *p = static_cast<int*>(a);
	int* p = reinterpret_cast<int*>(a);
	cout << p << endl;

	return 0;
}

4.3 const_cast

const_cast最常用的用途就是删除变量的const属性对应强制类型转换中有风险的去掉const属性),方便赋值。

int main()
{
	const int a = 2;
	int* p = const_cast<int*>(&a);
	*p = 3;
	cout << a << endl;
}

在这里插入图片描述

使用volatile可以确保编译器不会对这些变量的访问进行优化,从而确保每次访问都能读取到最新的值。

在这里插入图片描述


4.4 dynamic_cast

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

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

注意:

  1. dynamic_cast只能用于父类含有虚函数的类
  2. dynamic_cast会先检查是否能转换成功,能成功则转换,不能则返回nullptr
class A
{
public:
	virtual void f() {}
};
class B : public A
{};
void fun(A* pa)
{
	// dynamic_cast会先检查是否能转换成功,能成功则转换,不能则返回
	B* pb1 = static_cast<B*>(pa);
	B* pb2 = dynamic_cast<B*>(pa);
	cout << "pb1:" << pb1 << endl;
	cout << "pb2:" << pb2 << endl;
}

int main()
{
	A a;
	B b;
	fun(&a);
	fun(&b);
	return 0;
}

在这里插入图片描述


本篇文章的分享就到这里了,如果您觉得在本文有所收获,还请留下您的三连支持哦~

头像

标签:类型转换,const,深究,int,C++,a1,cast,a2
From: https://blog.csdn.net/2301_78843337/article/details/143247975

相关文章

  • c++时间形式转换
    https://cplusplus.com/reference/ctime/先放上官方文档。ctime类里,有很多转换时间格式的方法,下面只举例将UTC时间,转换为字符串的代码。‌‌Unix时间‌,也称为‌POSIX时间,是UNIX或类UNIX系统使用的时间表示方式。它从协调世界时1970年1月1日0时0分0秒起至现在的总秒数,不考虑闰秒......
  • C++:二叉搜索树进阶
    文章目录前言一、二叉搜索树的查找(递归版本)二、二叉树搜索树的插入(递归版本)三、二叉搜索树的删除(递归版本)四、析构函数五、拷贝构造六、赋值重载七、代码总结八、二叉搜索树性能对比九、key_value模型总结前言前面我们学习的二叉搜索树迭代的版本,今天我们来学习递归......
  • 南沙C++信奥赛陈老师解一本通题 1345:【例4-6】香甜的黄油
    ​ 【题目描述】农夫John发现做出全威斯康辛州最甜的黄油的方法:糖。把糖放在一片牧场上,他知道N(1≤N≤500)只奶牛会过来舔它,这样就能做出能卖好价钱的超甜黄油。当然,他将付出额外的费用在奶牛上。农夫John很狡猾。像以前的巴甫洛夫,他知道他可以训练这些奶牛,让它们在听到铃声时......
  • 奥数与C++小学四年级(第十五题 希望数)
    参考程序代码:#include<iostream>#include<vector>usingnamespacestd;//每个数字所需的火柴棍数量vector<int>matchsticks={6,2,5,5,4,5,6,3,7,6};//函数来计算一个数的火柴棍总数和数字和voidcheckHopeNumber(intnumber){inttotalMatchst......
  • ​Leetcode 166.珠宝的最高价值​ 网格图dp C++实现
    问题:Leetcode166.珠宝的最高价值现有一个记作二维矩阵 frame 的珠宝架,其中 frame[i][j] 为该位置珠宝的价值。拿取珠宝的规则为:只能从架子的左上角开始拿珠宝每次可以移动到右侧或下侧的相邻位置到达珠宝架子的右下角时,停止拿取注意:珠宝的价值都是大于0的。除非这个......
  • c++ string 识别标志位并解析标志位后面的字符
    解析字符串中的固定标志位正则表达式和iterator的配合应用#include<string>#include<map>#include<regex>#include<iostream>//替换\\M+后面的字符//\\M+195B6替换为文std::regexpattern(R"(\\M+[^\\M]*)");//匹配\\M+后跟任意非\\M的字符(0次或多次)......
  • C++多线程应用
    一个进程就是一个程序,一个程序里不止一个功能,每个功能的实现就可以交给一个线程去完成。一个进程就像是一个工程,这个工程里,有设计,有监理,有施工,就相当于三个线程,各干各的又相互配合。https://cplusplus.com/reference/thread/thread/thread/是C++的官方参考,个人觉得比较权威,比经......
  • C++ 模板专题 - 标签分派(Tag Dispatching)
    一:概述:        在C++中,TagDispatching是一种编程技巧,主要用于在编译期根据不同的类型或特征选择不同的函数重载或代码分支。TagDispatching借助类型标签(tags)进行函数调度,用于在模板中实现编译期的静态分派。这种方法特别适合在泛型编程中根据类型特性(如迭代器......
  • C++:二叉搜索树(迭代)
    文章目录前言一、二叉搜索树1.二叉搜索树的概念2.二叉搜索树的操作1)遍历2)查找3)插入4)删除二、二叉搜索树的实现(迭代版本)1.二叉搜索树的结构定义2.二叉搜索树的插入3.二叉搜索树遍历4.二叉搜索树删除5.二叉搜索树查找6.二叉搜索树代码总结总结前言今天来学......
  • 深入理解 C/C++ 中的 do-while 语句及其应用
    在C/C++编程中,do-while语句是一个重要的控制结构。它的独特之处在于,无论条件是否满足,循环体至少会执行一次。尽管do-while的基本用途是循环,但它在其他编程场合中同样具有非常巧妙和实用的应用。本文将探讨do-while语句的基本用法及其在宏定义和函数中的应用,提供高效......