C++中有四种形式的cast:const_caststatic_castdynamic_castreinterpret_cast。本篇文章将介绍什么时候和怎么样去使用这四种不同的cast。

1 const_cast

const_cast用于将一个const常量转换为变量,或者将一个变量转为const常量,即可以去除或者添加变量的常量性。

但是因为const_cast这一特性,应该尽量减少使用const_cast,如果一定要将一个const常量修改为一个变量,也要确定这种行为是正确的。

比如说

#include <iostream>
using namespace std;

// 1. Using a const_cast for a reference
void ExampleConstCast1(const int& value)
{
  // remove const specifier, access value
  int& ref = const_cast<int&> (value);
  ref = 20; // through a reference to a constant value, you can access value
  // value = 50; - not allowed, expression must be a modifiable lvalue
}

// 2. Using const_cast for a reference
void ExampleConstCast2(const int& value)
{
  // remove the const modifier from the value
  const_cast<int&> (value) = value + value;
}

// 3. Applying const_cast to a pointer
void ExampleConstCast3(const int* x)
{
  int* p = const_cast<int*> (x); // remove const from x
  *p = 100;
}

void main()
{
  // Demonstration of using the const_cast modifier
  int t = 30;
  ExampleConstCast1(t);
  cout << "t = " << t << endl; // t = 20

  ExampleConstCast2(t);
  cout << "t+t = " << t << endl; // t = 20+20 = 40

  int x = 50;
  ExampleConstCast3(&x);
  cout << "x = " << x << endl; // x = 100
}

程序输出

t = 20
t+t = 40
x = 100

2 static_cast

static_cast主要有如下几种用法:

  • 用于类层次结构中基类和子类之间指针或引用的转换。进行上行转换(把子类的指针或引用转换成基类)是安全的;进行下行转换(把基类指针或引用转换成子类指针或引用)时,由于没有动态类型检查,所以是不安全的。
  • 用于执行普通类型之间的隐式转换,比如将int转为float,char转为int
  • 将一个指针转换为void*,或者将void*转换为目标指针(不安全)
  • 将任何类型转为void类型

比如说:

普通类型转换

float a = static_cast<float>(5);
int b = static_cast<int>(9.63);

指针类型转换

OnEventData(void* pData)
{
  EventData *evtdata = static_cast<EventData*>(pData);
}

3 dynamic_cast

dynamic_cast专门用于处理多态类型,多态类型是指父类至少有一个声明或者继承的虚函数。

dynamic_cast使用运行时类型信息确定强制转换是否有效,与其他类型转换不同,dynamic_cast存在运行时开销。

dynamic_cast主要是在运行时将指针/引用在继承层次结构中安全的进行向上转换、向下转换、横向转换。如果指针类型转换失败将返回nullptr,如果引用类型失败则抛出std::bad_cast异常.

dynamic_cast主要是用在进行向下转换,基类转换到派生类安全转换中:

  • Derived * ptr_derived = dynamic_cast < Derived * > ( ptr_base )
  • Derived & ref_derived = dynamic_cast < Derived & > ( ref_base )

而向上转换,从派生类转换为基类则是安全的。

dynamic_cast在类层次间进行向上转换时,dynamic_cast和static_cast的效果是一样的;但是在进行向下转换时,dynamic_cast具有类型检查的功能,比static_cast更安全。

首先,dynamic_cast不能用于不是多态类型的转换,比如

#include<iostream>
using namespace std;

class Base  // 父类
{

};

class Child :public Base { // 子类

};


int main()
{
    Base* b_ptr = new Child();

    Child* c_ptr = dynamic_cast<Child*>(b_ptr);
}

编译错误,提示

error C2683: “dynamic_cast”:“Base”不是多态类型

我们来通过以下代码看dynamic_cast的用法:

  • 情况1,当基类指针实际上指向基类,使用dynamic_cast进行向下转换时,这个转换是不安全的
  • 情况2,当基类指针实际上指向派生类,使用dynamic_cast进行向下转换时,转换成功
  • 情况3,当子类指针实际上指向子类,使用dynamic_cast进行向上转换时,转换成功
#include<iostream>
using namespace std;

class Base  // 父类
{
public:
    virtual void f()
    {
        cout << "this is base class !" << endl;
    }
};

class Child :public Base { // 子类
public:
    void f()
    {
        cout << "this is child class !" << endl;
    }

};


int main()
{
    //情况1
    Base* b_ptr0 = new Base(); // 指向父类的父类指针
    Child* c_ptr0 = dynamic_cast<Child*>(b_ptr0);// 下行转换,此时由于父类指针实际上是指向父类的,
                                                //这个转换不安全,所以c_ptr0的值为nullptr
    if (!c_ptr0)
    {
        cout << "can not cast the type !" << endl; // 代码会走到这里
    }
    else
    {
        c_ptr0->f();
    }
    //情况2
    Base* b_ptr = new Child();// 指向子类的父类指针
    Child* c_ptr = dynamic_cast<Child*>(b_ptr); // 下行转换,此时由于父类指针实际上是指向子类的
                                                //所以这个转换是可以的
    if (!c_ptr)
    {
        cout << "can not cast the type !" << endl;
    }
    else
    {
        c_ptr->f();// 代码会走到这里
    }
    //情况3
    Child* c_ptr1 = new Child();// 指向子类的子类指针
    Base* b_ptr1 = dynamic_cast<Base*>(c_ptr1); // 将子类转换为父类是安全的,上行转换一定是允许的
    if (!b_ptr1)
    {
        cout << "can not cast the type !" << endl;
    }
    else
    {
        b_ptr1->f();// 代码会走到这里
    }
}

程序输出

can not cast the type !
this is child class !
this is child class !

4 reinterpret_cast

reinterpret_cast将一种类型直接强制转换为另一种类型,比如将一种指针直接转换为另一种指针,将一种类型直接转换为int。

与static_cast相比,reinterpret_cast的功能更加强大,安全性更低,对使用者的技术能力要求很高。

这个和const_cast一样,谨慎使用。

#include <iostream>
using namespace std;

void main()
{
  // 1. Convert char* => int
  int number;
  const char* pStr = "Hello world!";

  // get a pointer to str as an integer
  number = reinterpret_cast<int> (pStr);
  cout << "number = " << number << endl;

  // 2. Convert int => double*,
  // convert integer to pointer
  unsigned int num = 300;
  double* p;
  p = reinterpret_cast<double*> (num);
  cout << "p = " << p << endl;
}

程序输出

number = -1184584664
p = 000000000000012C

5 总结

对于上述四种cast,我们可以得出以下结论

  • const_cast通常用于去除常量的const属性
  • static_cast可以实现父类与继承子类之间的上行或者下行转换,但是不保证安全;可以进行普通类型转换,比如int转float;可以进行其他类型指针与void*类型的转换,不保证安全;
  • dynamic_cast主要用在存在多态类的转换,用于保证安全转换
  • reinterpret_cast用于在两个类型之间进行强制转换,不管这两个类型是否有关联,是最不安全的cast

参考链接