C++开发中级


811 浏览 5 years, 4 months

3.4.1 类型转换

版权声明: 转载请注明出处 http://www.codingsoho.com/

类型转换

使用( )的C风格类型转换在C++中仍然有效。然而,C++还提供了四种新的类型转换:Static_cast、dynamic_cast, const_cast以及reinterpret_cast。
您应该使用C++风格的类型转换而不是旧的C风格,因为它们会执行更多的类型检测,并且在语法上更加优秀。

本节讲述这些类型转换的目的,并指出使用它们的时机。

const_cast

const_cast最为直接。可以用这种类型转换舍弃变量的文本特性。这是四个类型转换中唯一可以舍弃文本特性的类型转换。当然从理论上来讲,并不需要const类型转换。如果某个变量是const,那么就应该一直是const。

然而实际上,有时候您会发现存在这种情况,某个函数需要采用const变量,但是又必须将这个变量传递给采用非const变量做参数的函数。“正确的”解决方案是在程序中保持const的一致,但是这并非唯一选择,特别是在使用第三方库的时候。 因此,有时需要舍弃变量的文本特性,下面是一个示例:

extern void ThirdPartyLibraryMethd(char* str);
void f(const char* str)
{
    ThirdPartyLibraryMethd(const_cast<char*>(str));
}
static_cast

可使用static_cast显式地执行C++语言直接支持的转换。例如,如果您编写了一个算术表达式,其中需要将int转换为double以避免整除,可以使用static_cast:

int i=3;
double result = static_cast<double>(i) / 10;

如果用户定义了相关的构造函数或者转换例程,您也可以使用static_cast执行显式的转换。例如,如果类A的拷贝构造函数将类B的对象作为参数,您可以使用static_cast将B对象转换为A对象。在许多情况下您需要这一行为,然而编译器会自动执行这个转换。

static_cast的另一种用法是在继承层次结构中执行向下转换。例如:

class Base
{
    public:
        Base();
        virtual ~Base(){}
};

class Derived : public Base
{
    public:
        Derived() {}
        virtual ~Derived() {}
};

int main()
{
    Base* b;
    Derived* d = new Derived();
    b = d; // Don't need a cast to go up the inheritance hierarchy
    d = static_cast<Derived*>(b); // Need a cast to go down the inheritance hierarchy
    Base base;
    Derived derived;
    Base & br = derived;
    Derived & dr = static_cast<Derived&>(br);
    return 0;
}

这种类型转换可以用于指针以及引用,而不适用于对象本身。

注意static_cast类型转换不执行运行期类型检测。这种方法允许您将任何Base指针转换为Derived指针,或者将Base引用转换为Derived引用,哪怕在运行时Base实际上并不是一个Derived也同样如此。

例如,下面的代码可以编译并执行,但是使用指针d可能导致灾难性的结果,包括内存开销超出了对象的边界。

Base * b = new Base();
Derived * b = static_cast<Derived*>(b);

为了执行具有运行时检测的更安全的类型转换,可以使用下一节介绍的dynamic_cast。

static_cast并不是全能的。您无法使用static_cast将某种类型的指针转换为不相关的其他类型的指针,无法将某种类型的对象直接转换为另一种类型的对象,无法将const类型转换为non-const类型,无法将指针转换为int。基本上您无法完成C++类型标准认为没有意义的转换。

reinterpret_cast

reinterpret_cast比static_cast功能更为强大,同时安全性也更差。您可以用它执行一些在技术上不被C++类型规则允许、但在某些情况下程序员又需要的类型转换。例如,可将某种指针类型转换为其他指针类型,即使这两个指针并不存在继承层次上的关系。这个关键字经常用于将指针转换为void*及其相反过程。与此类似,您也可以将某种类型的引用转换为其他类型的引用,即使这些类型之间并没有关系。下面是一些示例:

class X{};
class Y{};
int main()
{
    X x;
    Y y;
    X * xp = &X;
    Y * yp = &Y;
    // Need reinterpret cast for pointer conversion from unrelated classes
    // static_cast doesn't work
    xp = reinterpret_cast<X*>(yp);
    void* p = reinterpret_cast<Y*>(xp);
    xp = reinterpret_cast<X*>(p);    
    X& xr = x;
    Y& yr = reinterpret_cast<Y&>(x);
    return 0;
}

提示:从理论上说,您还可以使用reinterpret_cast将指针转换为int,或者将int转换为指针,但是这种程序被认为是不正确的,因为许多平台上(特别是64位的平台)指针以及int的大小不同。例如,在64位平台上,指针是64位,但整数可能是32位。将64位的指针转换为32位的整数会导致丢失32个重要的位。
使用reinterpret_cast时要特别小心,因为在执行转换时不会执行任何类型检测。

dynamic_cast

dynamic_cast为继承层次结构内的类型转换提供运行时检测。您可以用它来转换指针或者引用。dynamic_cast在运行时检测底层对象的类型信息。如果类型转换没有意义,dynamic_cast返回一个空指针(用于指针)或者抛出一个bad_cast异常(用于引用)

注意运行时类型信息存储在对象的虚表中。因此,为了使用dynamic_cast,类至少也要拥有一个虚方法。如果类没有虚表,尝试使用dynamic_cast会导致编译错误,这个错误有一点晦涩。例如,Microsoft VC++给出的错误是:

error C2683: 'dynamic_cast': 'MyClass' is not polymorphic type

下面是一些示例:

class Base
{
    public:
        Base();
        virtual ~Base(){}
};

class Derived : public Base
{
    public:
        Derived() {}
        virtual ~Derived() {}
};

int main()
{
    Base* b;
    Derived* d = new Derived();
    b = d; 
    d = dynamic_cast<Derived*>(b); // Need a cast to go down the inheritance hierarchy
    return 0;
}

下面的示例给出了dynamic_cast的错误用法:

下面用于引用的dynamic_cast将抛出一个异常。

Base base;
Derived derived;
Base & br = Base;
try{
    Derived & dr = dynamic_cast<Derived&>(br);
} catch(const bad_cast&){
    cout << "Bad Cast~\n";
}

注意您可以使用static_cast或者reinterpret_cast沿着继承层次结构向下执行同样的类型转换。
dynamic_cast的不同之处在于它会执行运行时(动态)类型检测,而static_cast以及reinterpret_cast甚至会执行不正确的类型转换。

类型转换总结

下表总结了在不同情形下应该使用的类型转换。