c++四种类型的转换

本文主要借鉴自cplusplus中关于类型转换的内容。


传统的类型转换主要有以下两种形式:

double x = 10.3;
int y;
y = int (x);    // functional notation
y = (int) x;    // c-like cast notation
  • 1
  • 2
  • 3
  • 4

这种形式的类型转换对于基本类型是足够了的。但是当面对复杂的类型时就显得捉襟见肘了,特别是对于c++中用于自定义的类类型。很多时候如果直接使用这种传统形式的类型转换可能会引发运行时错误。比如对于下面的例子:

// class type-casting
#include <iostream>
using namespace std;

class Dummy {
    double i,j;
};

class Addition {
    int x,y;
  public:
    Addition (int a, int b) { x=a; y=b; }
    int result() { return x+y;}
};

int main () {
  Dummy d;
  Addition * padd;
  padd = (Addition*) &d;
  cout << padd->result();
  return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22

这里将Dummy 类型的指针强制转化成Addition类型的指针,并赋值给padd。在VS2015中,这个程序编译是正确的,但是运行时要么出错要么会得到莫名其妙的结果。

为了控制c++ class之间的类型转换,C++提出了四种class类型转换机制,分别具有如下的形式:

dynamic_cast <new_type> (expression)
reinterpret_cast <new_type> (expression)
static_cast <new_type> (expression)
const_cast <new_type> (expression)
  • 1
  • 2
  • 3
  • 4

下面逐一来看一下这些class类型的转换机制具有什么特点。


dynamic_cast

dynamic_cast 只能用于class的指针或者引用之间的类型转换。dynamic_cast 保证转换后所得的指针指向的是一个完整的类实例。

也就是说,对于下面这种类型的转换:

pointer1 = dynamic_cast <pointer2> (expression)
  • 1

pointer1所指向的类实例中的所有成员都可以在pointer2的类实例中找到。以下面为例子:

#include <iostream>
#include <exception>
using namespace std;

class Base { virtual void dummy() {} };
class Derived: public Base { int a; };

int main () {
  try {
    Base * pba = new Derived;
    Base * pbb = new Base;
    Derived * pd;

    pd = dynamic_cast<Derived*>(pba);
    if (pd==0) cout << "Null pointer on first type-cast.\n";

    pd = dynamic_cast<Derived*>(pbb);
    if (pd==0) cout << "Null pointer on second type-cast.\n";

  } catch (exception& e) {cout << "Exception: " << e.what();}
  return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22

这个例子的输出是
Null pointer on second type-cast.

之所以第二个类型转换失败是因为pd 指向的类实例的所有成员不能在pbb指向的类实例中找到。根据声明可知,pd 指向派生类,pbb指向基类,显然基类是派生类的子集,派生类中的所有成员不可能都在基类中找得到。

那为什么第一个类型转换成功呢?我们看到

Base * pba = new Derived;
  • 1

pba虽然声明成的是一个基类指针,但是它实际上指向的是一个派生类的实例。自然同一个类的所有成员都可以在同一个类中找到。

按照上面的套路好像dynamic_cast只适合于将指向派生类的指针转换为指向基类的指针(upcast ),但是事实并非如此。

事实上,dynamic_cast也能够将基类指针转换成派生类的指针。但是必须满足一个条件:这个基类指针所指向的必须是一个派生类,其实就是上面例子所说的

综上,我们可以看到dynamic_cast转换提供了一个检查机制,它保证转换得到的指针所指向的内存是有效的。亦即按照该指针的声明类型进行内存操作不会产生意想不到的结果。


static_cast

static_cast can perform conversions between pointers to related
  classes

static_cast就比较随意了。它不提供检查机制。我们可以随意将指向基类的指针转换为指向派生类的指针,或者指向派生类的指针转化为指向基类的指针。比如对于下面的例子

class Base {};
class Derived: public Base {};
Base * a = new Base;
Derived * b = static_cast<Derived*>(a);
  • 1
  • 2
  • 3
  • 4

它不会像dynamic_cast那样保证转换后b指向的实例中的所有数据成员都有效。这点只能由程序员自己去做类型检查。

static_cast is also able to perform all conversions allowed implicitly (not only those with pointers to classes), and is also able to perform the opposite of these.

static_cast还支持所有其他的隐式类型转换。比如将void指针转换为其他类型指针,将整型,浮点型转换为枚举类型等等。


reinterpret_cast

reinterpret_cast converts any pointer type to any other pointer type,
  even of unrelated classes.

和static_cast类型转换机制相比,reinterpret_cast更随意了。它甚至允许不相关(不在一个继承体系内的类)类指针进行相互转换。比如这样

class A { /* ... */ };
class B { /* ... */ };
A * a = new A;
B * b = reinterpret_cast<B*>(a);
  • 1
  • 2
  • 3
  • 4

除此之外,我们还可以用reinterpret_cast将指针类型转换成整型数,或者将整型数转换成指针类型。当然,转换成的整型数的形式则是机器相关的。

reinterpret_cast可以简单理解为对一个二进制序列(指针或者其他类型变量的内存表示形式)的重新解释,比如将具有指针意义的二进制序列解释为具有整数意义的二进制序列,将指向类A的指针的二进制形式解释为指向类B的二进制形式。所以reinterpret_cast是极度机器相关的。很多时候不同机器得到的转换结果不一样。


const_cast

This type of casting manipulates the constness of the object pointed by a pointer, either to be set or to be removed. For example, in order to pass a const pointer to a function that expects a non-const argument:

最后这个比较简单,就是将const变量转换成非const型的变量;或者将非const变量转换成const变量。

#include <iostream>
using namespace std;

void print (char * str)
{
  cout << str << '\n';
}

int main () {
  const char * c = "sample text";
  print ( const_cast<char *> (c) );
  return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

当然这里的安全性也是要有程序员负责,因为将const变量转换成非const可能会使用户改变这个本不该改变的变量,使程序出现问题

猜你喜欢

转载自blog.csdn.net/caogenwangbaoqiang/article/details/80082349