C++进阶-类型转换

1. C语言中的类型转换

在C语言中,如果赋值运算符左右两侧类型不同,或者形参与实参类型不匹配,或者返回值类型与接收返回值类型不一致时,就需要发生类型转化,C语言中总共有两种形式的类型转换:隐式类型转换和显式类型转换

  1. 隐式类型转化:编译器在编译阶段自动进行,能转就转,不能转就编译失败
  2. 显式类型转化:需要用户自己处理

注意:C语言

  1. 隐式类型转换:整形之间 浮点数和整形之间
  2. 强制类型转换:指针之间 整形和指针
  3. 无关联类型是不支持转换的

2.C++中的类型转换

兼容C的转换用法:

  1. 内置类型 -》 自定义类型
  2. 自定义类型 -》 内置类型
  3. 自定义类型 -》 自定义类型
class A
{
public:
	A(int a1)
		:_a1(a1)
	{}

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

	operator int()
	{
		return _a1 + _a2;
	}

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

private:
	int _a1 = 1;
	int _a2 = 1;
};

class B
{
public:
	B(const A& aa)
		:_b1(aa.get())
	{}

private:
	int _b1;
};

int main()
{
	int i = 1;
	// 隐式类型转换
	double d = i;
	printf("%d, %.2f\n", i, d);
	int* p = &i;

	// 显示的强制类型转换
	int address = (int)p;
	printf("%p, %d\n", p, address);

	// 类型强制转换”: 无法从“int *”转换为“double”
	// double x = (double)p;

	// 内置类型 -> 自定义类型
	A aa1 = 1;
	// C++11
	A aa2 = {2,2};

	// 自定义类型 -> 内置类型
	int x = aa1;
	cout << x << endl;

	// 自定义类型 -> 自定义类型
	B bb = aa1;

	//while (cin>>x)
	//while (cin.operator>>(x))
	// 输入字符则就跳出循环
	// operator bool判断是否输入的类型正确
	while (cin.operator>>(x).operator bool())
	{
		cout << x << endl;
	}

	// 直接输入字符串
	string str;
	while (cin>>str)
	{
		cout << str << endl;
	}

	return 0;
}

为什么C++需要四种类型转换?
C风格的转换格式很简单,但是有不少缺点的:

  1. 隐式类型转化有些情况下可能会出问题:比如数据精度丢失
  2. 显式类型转换将所有情况混合在一起,代码不够清晰

因此C++提出了自己的类型转化风格,注意因为C++要兼容C语言,所以C++中还可以使用C语言的
转化风格

隐式类型转换的坑

// 隐式类型转换的坑
void Insert(size_t pos)
{
	int end = 10;
	while (end >= pos)
	{
		cout << end << endl;
		--end;
	}
}

int main()
{
	Insert(5);
	Insert(0);

	return 0;
}

由于参数是无符号整数,进行比较时会进行隐式类型转换,转换为无符号整数进行比较,根据底层,无符号整数小于0时,底层当作负数,按位取反+1,但是实质上还是无符号的整数去比较,则之后会将这个数据转化为正整数,则这个数会很大,所以会一直在循环中,之后会不断打印出来负数。

3. C++强制类型转换

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

3.1 static_cast

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

int main()
{

	int i = 1;
	// 隐式类型转换 : static_cast
	double d = static_cast<double>(i);
	printf("%d, %.2f\n", i, d);
	int* p = &i;

	return 0;
}

3.2 reinterpret_cast

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

int main()
{

	int i = 1;
	// 隐式类型转换 : static_cast
	double d = static_cast<double>(i);
	printf("%d, %.2f\n", i, d);
	int* p = &i;
	
	// 显示的强制类型转换 : reinterpret_cast
	int address = reinterpret_cast<int>(p);

	// error C2440: “static_cast”: 无法从“int *”转换为“int”
	// int address1 = static_cast(p);

	printf("%p, %d\n", p, address);
	
	// 类型强制转换”: 无法从“int *”转换为“double”
	// double x = (double)p;

	return 0;
}

3.3 const_cast

const_cast最常用的用途就是删除变量的const属性,方便赋值
C++进阶-类型转换_第1张图片
通常const修饰的数据是不可以被改变的,即使用上述方法进行了修改,但是输出的值还是没有修改以前的,就如,a已经通过指针进行了修改,但是输出a还是没有变化。

int main()
{
	volatile const int a = 1;
	// a++;
	int x = 0;

	cout << &a << endl;
	cout << &x << endl;

	//int* ptr = (int*)&a;
	int* ptr = const_cast<int*>(&a);
	(*ptr)++;

	cout << *ptr << endl;
	cout << a << endl;

	return 0;
}

C++进阶-类型转换_第2张图片
所以增加const_cast:从稳定变量变成可变变量,const属性已经去掉,a的值也就发生了改变。用const_cast时在前面的数据上加volatile,来保持稳定。

3.4 dynamic_cast

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

注意:

  1. dynamic_cast只能用于父类含有虚函数的类
  2. dynamic_cast会先检查是否能转换成功,能成功则转换,不能则返回0
// dynamic_cast
class A
{
public:
	virtual void f() {}

	int _a1 = 1;
};

class B : public A
{
public:
	int _b1 = 1;
};

void fun(A* pa)
{
	// pa指向B对象,转换成功
	// pa指向A对象,转换失败,返回空

	B* pb = dynamic_cast<B*>(pa);
	if (pb)
	{
		cout << pb << endl;
		pb->_b1++;
	}
	else
	{
		cout << "转换失败" << endl;
	}
}

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

	return 0;
}

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

4. RTTI(了解)

RTTI:Run-time Type identification的简称,即:运行时类型识别
C++通过以下方式来支持RTTI:

  1. typeid运算符
  2. dynamic_cast运算符
  3. decltype

5. 常见面试题

  1. C++中的4中类型转化分别是:static_castreinterpret_castconst_castdynamic_cast__
  2. 说说4中类型转化的应用场景。

你可能感兴趣的:(C++进阶,c++,开发语言)