前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >C++的类型转换

C++的类型转换

作者头像
小灵蛇
发布2024-06-06 21:39:10
970
发布2024-06-06 21:39:10
举报
文章被收录于专栏:文章部

前言:

今天我们来讲解C和C++的类型转换,内容炒鸡干,准备好水,一起来看看吧!

一. C语言中的类型转换

在C语言中,如果等号两边的类型不一样,或者形参和实参的类型不匹配,或者函数返回值与接收的变量类型不同,就会发生类型转换。C语言中存在两种类型转换:隐式类型转换和显示类型转换。

  • 隐式类型转换:编译器自动进行的,能转换就转,转换不了就会报错。
  • 显示类型转换:用户自己定义的。

1.1 隐式类型转换

隐式类型转换包括整型与整型之间,整型与浮点型之间,bool与整型之间,bool与指针之间的转换等等。

代码语言:javascript
复制
int i = 1;
//隐式类型转换
//整型与整型之间
char c = i;
//整型与浮点型之间
double d = i;
//bool与整型之间
bool b = i;
// bool与指针之间
int* p = &i;
if (p)
{
	printf("1");
}

特别注意的是:有关联的类型才能相互转换。

如下:

代码语言:javascript
复制
int i = 1;
int* p=&i;
//无法转换,他们之间没有关联
double dd = (double)p;

是会报错的,因为double类型的变量与int*类型的指针之间并没有关联,所以类型转换会失效。

1.2 强制类型转换

一般发生在不同类型的指针转换或者指针与整型转换之间:

代码语言:javascript
复制
int i = 1;
int* p = &i;

//显示的强制类型转换
//整型与指针之间
int address = (int)p;
//不同类型指针之间
double* pp=(double*)p;

1.3 缺点

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

比如下面这段代码:

代码语言:javascript
复制
//隐式类型转换
void Insert(size_t pos, int x)
{
	//size_t end = _size;
	int end = 10;
	//比较的时候end会隐式类型转换成size_t,再比较
	while (end >= pos)
	{
		cout << end << "挪走" << endl;
		--end;
	}
}
int main()
{
	Insert(0, 1);
	return 0;
}

这种情况下,在循环条件的判断时,int型的end会提升至size_t类型,那么就会导致,--之后永远不会小于0,就会导致死循环;虽然可以用强制转换end>=(int)pos,但是并不符合直觉。

二. C++中的类型转换

2.1 内置类型转换为自定义类型

内置类型转换为自定义类型,本质是采用构造函数,通过对构造函数传内置类型参数,转换为自定义类型。比如:string和const char*

2.2 自定义类型转换为内置类型

自定义类型转换为内置类型,本质是重载一个operator函数,例如下面:

代码语言:javascript
复制
class A
{
public:
	operator int()
	{
		return _a1 + _a2;
	}
private:
	int _a1 = 1;
	int _a2 = 2;
};

void test()
{
	A aa;
	int ii1 = aa;
	int ii2 = (int)aa;
}

2.3 自定义类型转换为自定义类型

发生这种转换的时候,我们只需要在类中加上相关参数的构造即可实现对应的类型转换(将参数设置为需要转换的类型的对象)

例如我们之前的initializer_list的构造方法:

特别注意的是:在我们的继承中,我们的派生类对象赋值给基类对象时,这个情况并不是类型转换,本质上是切片操作,千万不能混为一谈!!!

三. C++强制类型转换

标准C++为了增强类型转换的可观性,增添了四个强制类型转换操作符:static_cast,reinterpret_cast,const_cast,dynamic_cast。

3.1 static_cast

static_cast用于非多态类型的转换(静态转换),编译器隐式执行的任何类型转换都可用

static_cast,但它不能用于两个不相关的类型进行转换,这个也是我们之前在C语言类型转换中提及的。

代码语言:javascript
复制
int main()
{
  double d = 12.34;
  int a = static_cast<int>(d);
  cout<<a<<endl;
  return 0;
}

3.2 reinterpret_cast

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

代码语言:javascript
复制
int main()
{
 double d = 12.34;
 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);
 return 0;
}

3.3 const_cast

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

代码语言:javascript
复制
void Test ()
{
  const int a = 2;
  int* p = const_cast< int*>(&a );
  *p = 3;
  cout<<a <<endl;
}

有的小伙伴可能会有疑问,这个也是强制类型转换,为什么还要单独用一个操作符来转换呢?

我们来看看下面的代码:

代码语言:javascript
复制
int main()
{
    const int a2 = 2;
	int* p1 = (int*)&a2;
	*p1 = 3;
	cout << a2 << endl;
	cout << *p1 << endl;
	return 0;
}

运行结果:

我们发现好像结果不跟我们想的一样。来看看监视窗口:

咦?为什么监视窗口都是3,而输出就变为了2和3呢?

那是因为:编译器在这里可能会有一个优化,即当我们定义const变量时,编译器会把这个变量存到寄存器中,我们这里修改的是在内存中修改的, 而监视窗口是在内存中取的,所以看到监视窗口是正确的,而我们打印是打印寄存器的,所以打印出来并没有改变。

那么我们为了避免编译器优化导致的问题,我们可以在const变量前面加上volatile关键字修饰,表明这个const变量不会放到寄存器中。

代码语言:javascript
复制
int main()
{
	//强制类型转换,但是为什么把const单独拿出来
	//就是专门提醒,去掉const属性是有一些内存可见优化的风险,要注意是否加了volatile
	const int a2 = 2;
	int* p1 = const_cast<int*>(&a2);
	*p1 = 3;
	cout << a2 << endl;
	cout << *p1 << endl;
	return 0;
}

那么我们为了专门提醒,去掉const属性是有一些内存可见优化的风险,并要注意是否加了volatile,这也是我们为什么要加这个const_cast操作符的原因。

3.4 dynamic_cast

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

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

我们来看看:

代码语言:javascript
复制
class A
{
public:
	virtual void f() {}

	int _a = 0;
};

class B : public A
{
public:
	int _b = 1;
}; 
void fun(A* pa)
{
	B* pb = dynamic_cast<B*>(pa);
	if (pb)
	{
		cout << pb << endl;
		cout << pb->_a << endl;
		cout << pb->_b << endl;
	}
	else
	{
		cout << "转换失败" << endl;
	}
}
int main()
{
	A aa;
	B bb;
	fun(&aa);
	fun(&bb);
	return 0;

}

由于传参的时候,可以传基类对象,也可以传派生类对象,就有两种情况:

  • pa指向子类对象,转回子类,是安全的
  • pa指向父类对象,转回子类,是不安全的,存在越界的风险问题

那么我们就可以利用dynamic_cast事先检查转换能否成功,能成功就转换,不能成功就不转。

注意:

1. dynamic_cast只能用于父类含有虚函数的类 2. dynamic_cast会先检查是否能转换成功,能成功则转换,不能则返回

四. 谨慎使用强制转换

强制类型转换关闭或挂起了正常的类型检查,每次使用强制类型转换前,程序员应该仔细考虑是 否还有其他不同的方法达到同一目的,如果非强制类型转换不可,则应限制强制转换值的作用 域,以减少发生错误的机会。强烈建议:避免使用强制类型转换 。

总结:

好了,到这里今天的知识就讲完了,大家有错误一点要在评论指出,我怕我一人搁这瞎bb,没人告诉我错误就寄了。

祝大家越来越好,不用关注我(疯狂暗示)

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2024-06-06,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 一. C语言中的类型转换
    • 1.1 隐式类型转换
      • 1.2 强制类型转换
        • 1.3 缺点
        • 二. C++中的类型转换
          • 2.1 内置类型转换为自定义类型
            • 2.2 自定义类型转换为内置类型
              • 2.3 自定义类型转换为自定义类型
              • 三. C++强制类型转换
                • 3.1 static_cast
                  • 3.2 reinterpret_cast
                    • 3.3 const_cast
                      • 3.4 dynamic_cast
                      • 四. 谨慎使用强制转换
                      领券
                      问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档