[转]C++强制类型转换运算符(static_cast、reinterpret_cast、const_cast和dynamic_cast)

将类型名作为强制类型转换运算符的做法是C语言的老式做法,C++ 为保持兼容而予以保留。

C++ 引入了四种功能不同的强制类型转换运算符以进行强制类型转换:static_cast、reinterpret_cast、const_cast 和 dynamic_cast。

强制类型转换是有一定风险的,有的转换并不一定安全,如把整型数值转换成指针,把基类指针转换成派生类指针,把一种函数指针转换成另一种函数指针,把常量指针转换成非常量指针等。C++ 引入新的强制类型转换机制,主要是为了克服C语言强制类型转换的以下三个缺点。

  1. 没有从形式上体现转换功能和风险的不同。

例如,将 int 强制转换成 double 是没有风险的,而将常量指针转换成非常量指针,将基类指针转换成派生类指针都是高风险的,而且后两者带来的风险不同(即可能引发不同种类的错误),C语言的强制类型转换形式对这些不同并不加以区分。

  1. 将多态基类指针转换成派生类指针时不检查安全性,即无法判断转换后的指针是否确实指向一个派生类对象。

  2. 难以在程序中寻找到底什么地方进行了强制类型转换。

强制类型转换是引发程序运行时错误的一个原因,因此在程序出错时,可能就会想到是不是有哪些强制类型转换出了问题。

如果采用C语言的老式做法,要在程序中找出所有进行了强制类型转换的地方,显然是很麻烦的,因为这些转换没有统一的格式。

而用 C++ 的方式,则只需要查找_cast字符串就可以了。甚至可以根据错误的类型,有针对性地专门查找某一种强制类型转换。例如,怀疑一个错误可能是由于使用了 reinterpret_cast 导致的,就可以只查找reinterpret_cast字符串。

C++ 强制类型转换运算符的用法如下:

强制类型转换运算符 <要转换到的类型> (待转换的表达式)

例如:

double d = static_cast <double> (35); //将 35 的值转换成实数

下面分别介绍四种强制类型转换运算符。

static_cast

static_cast 用于进行比较“自然”和低风险的转换,如整型和浮点型、字符型之间的互相转换。另外,如果对象所属的类重载了强制类型转换运算符 T(如 T 是 int、int* 或其他类型名),则 static_cast 也能用来进行对象到 T 类型的转换。

static_cast 不能用于在不同类型的指针之间互相转换,也不能用于整型和指针之间的互相转换,当然也不能用于不同类型的引用之间的转换。因为这些属于风险比较高的转换。

static_cast 用法示例如下:

1.  #include <iostream>
2.  u[sin](http://c.biancheng.net/ref/sin.html)g namespace std;
3.  class  A
4.  {
5.  public:
6.  operator int() { return 1; }
7.  operator char*() { return NULL; }
8.  };
9.  int main()
10.  {
11.  A  a;
12.  int n;
13.  char* p = "New Dragon Inn";
14.  n = static_cast <int> (3.14);  // n 的值变为 3
15.  n = static_cast <int> (a);  //调用 a.operator int,n 的值变为 1
16.  p = static_cast <char*> (a);  //调用 a.operator char*,p 的值变为 NULL
17.  n = static_cast <int> (p);  //编译错误,static_cast不能将指针转换成整型
18.  p = static_cast <char*> (n);  //编译错误,static_cast 不能将整型转换成指针
19.  return 0;
20.  }

reinterpret_cast

reinterpret_cast 用于进行各种不同类型的指针之间、不同类型的引用之间以及指针和能容纳指针的整数类型之间的转换。转换时,执行的是逐个比特复制的操作。

这种转换提供了很强的灵活性,但转换的安全性只能由程序员的细心来保证了。例如,程序员执意要把一个 int* 指针、函数指针或其他类型的指针转换成 string* 类型的指针也是可以的,至于以后用转换后的指针调用 string 类的成员函数引发错误,程序员也只能自行承担查找错误的烦琐工作:(C++ 标准不允许将函数指针转换成对象指针,但有些编译器,如 Visual Studio 2010,则支持这种转换)。

reinterpret_cast 用法示例如下:

1.  #include <iostream>
2.  using namespace std;
3.  class  A
4.  {
5.  public:
6.  int i;
7.  int j;
8.  A(int n):i(n),j(n) { }
9.  };
10.  int main()
11.  {
12.  A  a(100);
13.  int &r = reinterpret_cast<int&>(a); //强行让 r 引用 a
14.  r = 200;  //把 a.i 变成了 200
15.  cout << a.i << "," << a.j << endl;  // 输出 200,100
16.  int n = 300;
17.  A  *pa = reinterpret_cast<A*> ( & n); //强行让 pa 指向 n
18.  pa->i = 400;  // n 变成 400
19.  pa->j = 500;  //此条语句不安全,很可能导致程序崩溃
20.  cout << n << endl;  // 输出 400
21.  long long la = 0x12345678abcdLL;
22.  pa = reinterpret_cast<A*>(la); //la太长,只取低32位0x5678abcd拷贝给pa
23.  unsigned int u = reinterpret_cast<unsigned int>(pa);//pa逐个比特拷贝到u
24.  cout << hex << u << endl;  //输出 5678abcd
25.  typedef void (* PF1) (int);
26.  typedef int (* PF2) (int,char *);
27.  PF1  pf1;  PF2  pf2;
28.  pf2 = reinterpret_cast<PF2>(pf1); //两个不同类型的函数指针之间可以互相转换
29.  }

程序的输出结果是:

200, 100
400
5678abed

第 19 行的代码不安全,因为在编译器看来,pa->j 的存放位置就是 n 后面的 4 个字节。 本条语句会向这 4 个字节中写入 500。但这 4 个字节不知道是用来存放什么的,贸然向其中写入可能会导致程序错误甚至崩溃。

上面程序中的各种转换都没有实际意义,只是为了演示 reinteipret_cast 的用法而已。在编写黑客程序、病毒或反病毒程序时,也许会用到这样怪异的转换。

reinterpret_cast体现了 C++ 语言的设计思想:用户可以做任何操作,但要为自己的行为负责。

const_cast

const_cast 运算符仅用于进行去除 const 属性的转换,它也是四个强制类型转换运算符中唯一能够去除 const 属性的运算符。

将 const 引用转换为同类型的非 const 引用,将 const 指针转换为同类型的非 const 指针时可以使用 const_cast 运算符。例如:

1.  const string  s = "Inception";
2.  string& p = const_cast <string&> (s);
3.  string* ps = const_cast <string*> (&s);  // &s 的类型是 const string*

dynamic_cast

用 reinterpret_cast 可以将多态基类(包含虚函数的基类)的指针强制转换为派生类的指针,但是这种转换不检查安全性,即不检查转换后的指针是否确实指向一个派生类对象。dynamic_cast专门用于将多态基类的指针或引用强制转换为派生类的指针或引用,而且能够检查转换的安全性。对于不安全的指针转换,转换结果返回 NULL 指针。

dynamic_cast 是通过“运行时类型检查”来保证安全性的。dynamic_cast 不能用于将非多态基类的指针或引用强制转换为派生类的指针或引用——这种转换没法保证安全性,只好用 reinterpret_cast 来完成。

dynamic_cast 示例程序如下:

1.  #include <iostream>
2.  #include <string>
3.  using namespace std;
4.  class  Base
5.  {  //有虚函数,因此是多态基类
6.  public:
7.  virtual ~Base() {}
8.  };
9.  class  Derived : public Base { };
10.  int main()
11.  {
12.  Base  b;
13.  Derived  d;
14.  Derived* pd;
15.  pd = reinterpret_cast <Derived*> (&b);
16.  if (pd == NULL)
17.  //此处pd不会为 NULL。reinterpret_cast不检查安全性,总是进行转换
18.  cout << "unsafe reinterpret_cast" << endl; //不会执行
19.  pd = dynamic_cast <Derived*> (&b);
20.  if (pd == NULL)  //结果会是NULL,因为 &b 不指向派生类对象,此转换不安全
21.  cout << "unsafe dynamic_cast1" << endl;  //会执行
22.  pd = dynamic_cast <Derived*> (&d);  //安全的转换
23.  if (pd == NULL)  //此处 pd 不会为 NULL
24.  cout << "unsafe dynamic_cast2" << endl;  //不会执行
25.  return 0;
26.  }

程序的输出结果是:
unsafe dynamic_cast1

第 20 行,通过判断 pd 的值是否为 NULL,就能知道第 19 行进行的转换是否是安全的。第 23 行同理。

如果上面的程序中出现了下面的语句:

Derived & r = dynamic_cast <Derived &> (b);

那该如何判断该转换是否安全呢?不存在空引用,因此不能通过返回值来判断转换是否安全。C++ 的解决办法是:dynamic_cast 在进行引用的强制转换时,如果发现转换不安全,就会拋出一个异常,通过处理异常,就能发现不安全的转换。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,602评论 6 481
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,442评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 152,878评论 0 344
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,306评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,330评论 5 373
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,071评论 1 285
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,382评论 3 400
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,006评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,512评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,965评论 2 325
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,094评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,732评论 4 323
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,283评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,286评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,512评论 1 262
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,536评论 2 354
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,828评论 2 345

推荐阅读更多精彩内容