C++ 类型转换

1. C语言中的类型转换

C 语言中,如果 赋值运算符左右两侧类型不同,或者形参与实参类型不匹配,或者返回值类型与 接收返回值类型不一致时,就需要发生类型转化 C 语言中总共有两种形式的类型转换: 隐式类型
转换和显式类型转换
1. 隐式类型转化:编译器在编译阶段自动进行,能转就转,不能转就编译失败
2. 显式类型转化:需要用户自己处理,格式为 (type_name)  expression,其中 type_name 是目标类型,expression是要转换的表达式。
void Test()
{
	int i = 1;
	// 隐式类型转换
	double d = i;
	printf("%d, %.2f\n", i, d);
	int* p = &i;
	// 显示的强制类型转换
	int address = (int)p;
	printf("%x, %d\n", p, address);
}
C 风格的转换格式很简单,但是有不少缺点的:
1. 隐式类型转化有些情况下可能会出问题:比如数据精度丢失
2. 显式类型转换将所有情况混合在一起,代码不够清晰

2. C++的类型转换

C语言中的类型转换都是内置类型之间的转换,到C++后,出现了类的概念,于是又多出了以下与类相关的转换:

  1. 内置类型 → 自定义类型,依赖构造函数
  2. 自定义类型 → 自定义类型,依赖构造函数
  3. 自定义类型 → 内置类型,依赖操作符重载

前两种比较简单,如下

class A
{
public:
	A(const int& sta)
		:_sta(sta)
	{}
private:
	int _sta;
};
int main()
{
	A tmp = 10;
	return 0;
}

第三种,则需要通过操作符重载来实现,如下

class A
{
public:
	A(const int& a, const int& b)
		:_a(a)
		,_b(b)
	{}
	operator int()
	{
		return _a + _b;
	}
	operator double()
	{
		return _a - _b;
	}
private:
	int _a;
	int _b;
};
int main()
{
	A tmp(5, 10);
	int t1 = tmp;
	double t2 = tmp;
	return 0;
}

通过操作符重载让类A支持两种类型转换:A->intA->double,格式为operator type_name (),operator type_name就可以完成到type_name类型的转换。注意的是,类型转换的返回值是固定的,所以operator type_name左侧不用写函数返回类型。

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

2.1 static_cast

static_cast用于非多态类型的转换(静态转换),例如基本数据类型之间的转换,或者在类层次结构中进行上行转换(基类指针或引用转换为派生类指针或引用),这种上行转换是安全的。但其不进行运行时类型检查,因此在进行下行转换(派生类指针或引用转换为基类指针或引用)时,如果转换不合法,编译器不会报错,但在运行时可能会导致未定义行为. 编译器隐式执行的任何类型转换都可用 static_cast ,但它不能用于两个不相关的类型进行转换.

它的作用包括:

  • 基本数据类型之间的转换,如将 double 转换为 int
int main()
{
	double a = 5.5;
	int b = static_cast<int>(a);
	return 0;
}
  • 类层次结构中的向上转型,即将子类类型的指针或引用转换为基类类型。
class Base
{
    //...
};
class Derived : public Base
{
    //...
};
int main()
{
    Derived a;
    Base* b = static_cast<Base*>(&a);
    return 0;
}
  • 指针或引用类型之间的转换,但不支持多态类型的向下转型。
int main()
{
    void* a = new int;
    int* b = static_cast<int*>(a);
    return 0;
}
static_cast 不执行运行时类型检查,因此在使用时需要程序员确保转换安全。即 static_cast 用在类型之间进行安全的转换

2.2 reinterpret_cast

reinterpret_cast 是 C++ 中用于执行底层的、不安全的类型转换的运算符。它可以将指针类型转换为整数类型,或者将整数类型转换为指针类型,也可以在指针类型之间进行转换,但不进行类型安全检查。 其通常为操作数的位模式提供较低层次的重新解释,用于将一种类型转换为另一种不同的类型.
  • 将一个指针转换为整型
int main()
{
    int* a = new int(5);
    int b = reinterpret_cast<int>(a);
    return 0;
}
  • 在不同的指针类型之间进行转换
int main()
{
    int* a = new int(5);
    double* b = reinterpret_cast<double*>(a);
    return 0;
}

2.3 const_cast

const_cast 是 C++ 中的一个类型转换运算符,用于移除或添加 const 或 volatile 限定符。需要注意的是,const_cast 中的类型必须是指针、引用或指向对象类型成员的指针。
它主要用于以下几种情况:

  • 当你需要传递一个 const 对象到一个期望非 const 参数的函数时,可以使用 const_cast 来去除 const 限定符。
void func(int& tmp)
{}
int main()
{
	const int a = 5;
	int* b = const_cast<int*>(&a);
	func(*b);
	return 0;
}
  • 在某些设计中,可能需要修改一个原本声明为 const 的对象,这时可以使用 const_cast 来去除 const 限定符,但这种做法应该非常谨慎,因为它可能会导致未定义行为,尤其是当原始对象实际上是存储在常量内存区域时。
int main()
{
    const int a = 5;
    int* b = const_cast<int*>(&a);
    *b = 10; // 修改常量对象的值
    cout << "a:" << a << endl;
    cout << "b:" << *b << endl;
    return 0;
}

如上代码中a为const对象,我们使用const_cast 来去除 const 限定符得到b,并修改b的值为10,再打印出来时却发现a仍为5。为什么会这样呢?

实际上,C++的常量不存储在常量区,而是存储在栈区,而编译器会对const变量优化,把const变量放到寄存器中,后续只要访问变量a,都去寄存器中查找。我们通过指针b修改的变量,其实修改的是栈区中的数据,没有修改寄存器的数据,因此访问a还是从寄存器中读出了5

为了解决这个问题,可以用volatile 关键字修饰const变量,此时就不会把常量存在寄存器中了。

int main()
{
    volatile const int a = 5;
    int* b = const_cast<int*>(&a);
    *b = 10; // 修改常量对象的值
    cout << "a:" << a << endl;
    cout << "b:" << *b << endl;
    return 0;
}

同样的可以使用 const_cast 来去除 volatile 限定符

int main()
{
    volatile const int a = 5;
    const int* b = const_cast<const int*>(&a);
    return 0;
}

2.4 dynamic_cast

dynamic_cast 用于将一个父类对象的指针 / 引用转换为子类对象的指针或引用 ( 动态转换 )
向上转型:子类对象指针 / 引用 -> 父类指针 / 引用 ( 不需要转换,赋值兼容规则 )
向下转型:父类对象指针 / 引用 -> 子类指针 / 引用 ( dynamic_cast 转型是安全的 )
注意:
1. dynamic_cast 只能用于父类含有虚函数的类
2. dynamic_cast会先检查是否能转换成功,能成功则转换,不能则返回空指针(对于指针类型)或抛出std::bad_cast异常(对于引用类型)。
进行向下转型时, dynamic_cast 提供类型安全检查,这是 static_cast 不具备的。 static_cast  在编译时进行类型检查,但不进行运行时检查,因此可能导致未定义行为。相比下 dynamic_cast  更加安全,尤其是在处理复杂的类层次结构时.
class Base
{
public:
	virtual void func()
	{}
};
class Derived : public Base
{
	//...
};
int main()
{
	Base a;
	Derived b;
	Base* c = dynamic_cast<Base*>(&b);//向上转型
	Derived* d = dynamic_cast<Derived*>(&a);//向下转型
	cout << "c:" << c << endl;
	cout << "d:" << d <<endl;
	return 0;
}
我们可以看到的是c转型成功,而d转型失败,为空指针。

3.运行时类型信息(RTTI)

运行时类型信息(RTTI)是C++中的一项特性,它允许程序在运行时识别和检查对象的类型。

C++ 通过以下方式来支持 RTTI
1. typeid 运算符
2. dynamic_cast 运算符
3. decltype
  • 13
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

我要满血复活

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值