呵呵,今天来好好看看着几个转换操作符的用法。以前老是看着眼熟,但是用着手生。今天决定搞定这些个东西。

在C语言中类型转换有几种方式:

1.      (expression). 在表达式外边加括号,由编译器来决定怎么改变。

2.      new_type(expression). 强制类型括号住表达式。

3.      (new_type)expression. 括号住强制类型。

4.      C语言允许的内置转换。

这些转换非常高效,我非常喜欢使用。特别是在指针转换和数值转换时用到的非常多。只要编写程序的人知道自己要做什么转换,并知道应该怎样转换的话,我认为上边的转换方式非常之好。但是没有清楚的了解每个转换的细节的话,就有可能出现问题,比如指针指向不应该指向的区域:出现野指针或者指向位置错误(主要是对内存结构不了解),或者计算数值被截去等问题发生。

C++程序兼容C语言的转化,但是针对面向对象语言的特性,设计了以下几个类型转换操作符。他们的出现是为了C语言类型转换中语义模糊和固有的危险陷阱,因为C语言不去判断所要操作的类型转换是否合理。

static_cast:用于非多态类型的转换。

dynamic_cast:用于多态类型的转换。

const_cast:用来消除const, volatile, __unaligned属性的转换。

reinterpret_cast:用于空间的重新解释。

还有一个在VS2005中出现的类型转换关键字safe_cast.#2

 

static_cast:

static_cast<type_id>(expression)

这个关键字可以用来将一个指针转换为父类的指针也可以转换为子类的指针或者基本的类型转换。但是这种转换是强制的,并没有任何运行时类型检查来保证转换的正确性,所以编写代码的人需要明白自己所进行的转换是否合理。

//基本类型的转换

enum e { A = 1, B, C };

double d = 12.25;

unsigned int ui = 25;

char c = static_cast<char>(ui);

int i = static_cast<int>(d);

int j = static_cast<int>(B);

//父类子类转换

class F                  //father

{

public:

    int _father;

};

class S : public F       //son

{

public:

    _son;

};

F *pFather = new F();

S *pSon = new S();

F *pF;

S *pS;

pF = static_cast<F *>(pSon);    //将子类指针转换为父类指针,OK

pS = static_cast<S *>(pFather); //将父类指针转换为子类指针,错误

第二个错误的转换不是说编译器编译不过去,而是运行时会出现错误。

原因如下:假设pF指向了pSon的位置,它可以访问_father,它找不到_son,这样没有问题。但是pS指向了pFather的位置,它访问_father没有问题,但是访问_son时就会产生错误,因为pFather根本没有_son这个变量。

下面是将父类转换为子类指针时,static_cast和dymanic_cast两者不同的表现:

class F

{

public:

    virtual void speak(){};

    int i;

};

 

class S : public F

{

public:

    void speak()

    {

       cout << "S = " << _s << endl;

    }

    double _s;

};

F *pF = new F();

S *pS = static_cast<S*>(pF);

pS->speak();

S1 *pDS = dynamic_cast<S*>(pF);

pDS->speak();

静态的转换编译不显示警告,运行结果不输出(调用F的speak函数)。动态的转换编译显示可能出现不可预期的结果,运行时崩溃。(VS2005时,返回空指针,但是不会崩溃。我认为要是按照C++的特性还是崩溃比较好一点,让程序员容易理解这么做是错误的。)

 

dynamic_cast:

dynamic_cast<type_id>(expression)

本关键字主要处理多态的类型转换,type_id要么是指针类型,要么是引用类型要么是void*。当type_id为指针和void*时,expression必须是type_id类型的指针,当type_id为引用时,expression也必须是type_id类型的引用。#1

 

1.最常用的用法就是将子类指针转换为父类指针。(不举例)

2.当type_id为void*时,指针指向整个对象的空间。

Class A;

A *pA = new A();

void *p = dynamic_cast<void*>(pA);

但是type_id不为void*时,计算机就要在运行时检查是否能够转换。

3.跳级转换。

class A{};

class B : public A{};

class C : public B{};

    A *pA;

    B *pB;

    C *pC = new C();

    pB = dynamic_cast<B*>(pD);  //逐级转换OK

    pA = dynamic_cast<A*>(pB);  //逐级转换OK

    或者

    pA = dynamic_cast<A*>(pC);  //跳级转换OK

    delete pD;

以下情况跳级转换不可以:

class A{};

class B : public A{};

class C : public A{};

class D : public B, public C{};

    A *pA;

    D *pD = new D();

    pA = dynamic_cast<A*>(pB);  //出现错误,是不行的,原因大家都清楚。

 

class A{};

class B : public A{};

class C : public A{};

class D : public B{};

class E : public C, public D{};

    A *pA;

    B *pB;

    E *pE = new E();

    pB = dynamic_cast<B*>(pE);

    pA = dynamic_cast<A*>(pB);  //可以

    pA = dynamic_cast<A*>(pE);  //不可以,原因是很简单的。

    delete pE;

4.两个不相干的类之间转换。

class A {};

class B {};

    A* pa = new A;

    B* pb = dynamic_cast<B*>(pa);   // 不可以,没有相互转换的基础

但是reinterpret_cast可以转换,可以参考reinterpret_cast

 

 

const_cast:

const_cast<type_id>(expression)

这个关键字消除了几个关键字的作用const, volatile,和__unaligned的作用。const经常使用。MSDN有const的例子照抄过来。

class CCTest {

public:

   void setNumber( int );

   void printNumber() const;

private:

   int number;

};

void CCTest::setNumber( int num ) { number = num; }

void CCTest::printNumber() const {

   cout << "/nBefore: " << number;

   const_cast< CCTest * >( this )->number--;//这里消除了const的作用

   cout << "/nAfter: " << number;

}

int main() {

   CCTest X;

   X.setNumber( 8 );

   X.printNumber();

}

 

reinterpret_cast:

reinterpret_cast

这个关键字比较“强悍”,随意转换类型。但是转换错误,就是你的不对了。呵呵,我的原则两个字:“慎用”。

这个关键字可以在任何类型的指针之间转换。

不可以替代const_cast。

不提供安全转换。

MSDN的例子显示出来它的强悍,也显示出了他的脆弱。只要你一个不小心就会乱用。

#include <iostream>

 

// Returns a hash code based on an address

unsigned short Hash( void *p ) {

   unsigned int val = reinterpret_cast<unsigned int>( p );

   return ( unsigned short )( val ^ (val >> 16));

}

 

using namespace std;

int main() {

   int a[20];

   for ( int i = 0; i < 20; i++ )

      cout << Hash( a + i ) << endl;

}

 

 

#1 dynamic_cast的type_id为引用的情况我不准备了解,好像是微软VS2005的特性,而不是标准C++的特性。对于VS6.0我还是感觉比较欣赏的。虽然不如他的新版本那样支持更多的C++特性,但是我自己的感觉是VS的产品在无限的向C#靠拢,无限的向java的易用性靠拢,这样的话C++程序在抑制到别的操作系统时就需要做很大的修改。这也是微软的霸道之处。题外话:微软的vista是一款失败的产品,在vista上微软开发了virtual PC 2007的虚拟机,但是这款产品只支持windows系统的产品安装,对于linux产品他就不支持(只能安装,不能用),因为他不支持24位真彩色。这就说明它的心态是封闭的,而封闭最终导致它的衰败。


#2 safe_cast也是微软的东西,想了解的请参考VS2005的MSDN。