C++:新—类型转换

目录

C/C++的旧式类型转换

C++的现代类型转换

静态类型转换 static_cast

重新解释转换 reinterpret_cast

常量类型转换 const_cast

动态类型转换 dynamic_cast


C/C++的旧式类型转换

在C语言中,类型转换被分为显式和隐式,常见的类型转换如下:

  1. 整型之间,intlong longlongshrotchar等互相转换
  2. 整型与浮点数之间,intlong等与floatdouble之间的转换
  3. 整型与布尔值,非0为true,0为false
  4. 指针与布尔值,空指针为false,非空指针为true
  5. 整型与指针之间
  6. 不同指针之间

C++后,增加了类的概念,类型就丰富了起来,于是又多出了以下与类相关的转换:

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

C++支持通过操作符重载操作符重载操作符重载来进行自定义类型 → 内置类型的转换

比如:

class A
{
public:
    operator int()
    {
        return _a + _b;
    }

    operator double()
    {
        return _a / _b;
    }
private:
    int _a = 3;
    int _b = 5;
};

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

A aa;
int x = aa;
double y = aa;

以上所有类型转换,都是旧式的,是基础C语言的显式与隐式的规则,衍生出来的各种类型转换。

C语言风格的类型转换很简洁,但是也有缺点:

  1. 转换的分类过于笼统,只分显式和隐式
  2. 隐式转换非常坑,很容易出bug

C++的现代类型转换

C++提供了四种类型转化,以更加细致安全的方式来区分各种类型转换。

静态类型转换 static_cast

用途:

在类型之间进行安全的转换

格式:

static_cast<type>(expression)

任何能够明确的类型转换都可以使用static_caststatic_cast不能转换掉底层constvolatile),以上说明中,安全的转换不是指编译器帮你确认安全,而是说程序员自己确定了这个转换是安全的之后,再用static_cast

特点在于:不提供运行时的检查,所以叫静态类型转换 

主要在以下几种场合中使用:

  • 整型浮点型的转换(原先的隐式转换):
int x = 10;
double y = static_cast<double>(x);

只要是隐式转换可以做到的,都推荐改用static_cast

  • 派生类基类的转换(上行转换):
class Base
{
    //...
};
class Derived : public Base
{ 
    //...
};

int main()
{
    Derived d;
    Base* b = static_cast<Base*>(&d);

    return 0;
}
  • void*具体类型指针的转换(指针之间的转换):
void* p = malloc(sizeof(int));
int* x = static_cast<int*>(p);

重新解释转换 reinterpret_cast

用途:

进行不安全的类型转换

格式:

reinterpret_cast<type>(expression)

这是非常激进的指针类型转换,在编译期完成,可以转换任何类型的指针所以极不安全。 

它仅仅重新解释内存中的二进制表示,而不执行任何实际的类型检查,因此非常危险,不建议使用。

  • 将一个指针转换为整型:
int x = 1;
int p = reinterpret_cast<int>(&x);
  • 在不同的指针类型之间进行转换:
int d;
double* p = reinterpret_cast<double*>(&d);

常量类型转换 const_cast

用途:

移除或添加对象的const属性和volatile属性

格式:

const_cast<type>(expression)
  • 移除const属性:
const int x = 10;
int* p = const_cast<int*>(&x);
*p = 20; // 修改常量对象的值
cout << x << endl;
cout << *p << endl;

输出结果:

10
20

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

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

volatile const int x = 10;
int* p = const_cast<int*>(&x);
*p = 20;

cout << x << endl;
cout << *p << endl;

输出结果为:

20
20

  • 消除volatile属性
volatile const int x = 10;
const int* p = const_cast<const int*>(&x);

动态类型转换 dynamic_cast

用途:

在继承层次结构中进行安全的向下转换,检查转换是否成功。

格式:

dynamic_cast<type>(expression)

相比static_castdynamic_cast会在运行时检查类型转换是否合法,具有一定的安全性。由于运行时的检查,所以会额外消耗一些性能。 

dynamic_cast常用于基类与派生类之间的转换,分为两种情况:

上行转换:从 派生类指针/引用 向 基类指针/引用 的转换,本质上是赋值兼容,一般来说是安全的
下行转换:从 基类指针/引用 向 派生类指针/引用 的转换,安全性不确定

一般来说,上行转换使用static_cast即可,因为这是一个比较安全的行为。而下行转换用dynamic_cast来检测安全性。

  1. 使用dynamic_cast要求基类中必须有虚函数!
  2. 如果转换成功,dynamic_cast返回转换后的指针,如果转换失败,dynamic_cast返回空指针。
  • 向下转换,并检查转换是否成功:
class Base
{
    virtual void foo() {}
};

class Derived : public Base
{
    /* ... */
};

int main()
{
    Base* b = new Base();
    Derived* d = new Derived();

    Base* p1 = static_cast<Base*>(d);//上行转换

    Derived* p2 = dynamic_cast<Derived*>(p1);
    Derived* p3 = dynamic_cast<Derived*>(b);

    cout << p2 << endl;
    cout << p3 << endl;

    return 0;
}

以上代码中,完成了两次下行转换,分别是p1 -> p2d -> p3的转换。而p1是通过b转化而来的,所以p1是指向派生类的基类指针,而d是指向基类的基类指针。

因此p1转回派生类指针,是合理的,而d转为派生类指针,是危险的,于是dynamic_cast会阻止d转为派生类指针。 

0000015220F64100
0000000000000000

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值