https://stackoverflow.com/questions/103512/why-use-static-castintx-instead-of-intx

https://stackoverflow.com/questions/2253168/dynamic-cast-and-static-cast-in-c

C 风格的强制类型转换形如 (type)(var),而 C++ 提出了四种不同的用于强制类型转换的操作符static_cast<type>(var)const_cast<type>(var)dynamic_cast<type>(var)reinterpret_cast<type>(var)

那 C++ 为什么要新引入这四种类型转换的操作符呢?原因在于 C 风格的类型转换适用范围太广,其中有些转换是相对安全的,例如 char 转换成 int,但也有一些类型转换是相对危险的,例如将指向基类的指针转换成派生类的指针。所以 C++ 要求程序员根据不同的情况选用不同的类型转换方式,从而增强类型的安全性。

static_cast<type>(var)

相对安全的类型转换都可以通过 static_cast 完成,这里的相对安全是和其他三种类型类型转换相对而言的。一些基本的类型转换例如 int 转换成 char 都可以使用 static_cast 完成。

static_cast 唯一允许的一种比较危险的类型转换是将基类指针转换成派生类指针,不管这个指针到底指向的是一个派生类对象还是基类对象。另一种类型转换方式 dynamic_cast 为这种类型转换提供了更高的安全性。

dynamic_cast<type>(var)

如上所述,dynamic_cast 可以为类指针之间的强制转换提供更高的安全性。

对于指向派生类的指针转换成指向基类指针这种情况,可以知道这种转换一定是安全的,所以用 static_cast 或者 dynamic_cast 都无所谓。而将指向基类的指针转换成派生类指针这种情况就可能会有安全性的问题。看下面的例子:

class Base
{
public:
    virtual void func() {}
};

class DerivedA : public Base
{
public:
    virtual void func() {}
};

class DerivedB : public Base
{
public:
    virtual void func() {}
};

int main()
{
    Base* ptrA = new DerivedA;

    DerivedA* realA = dynamic_cast<DerivedA *>(ptrA);
    DerivedB* realB = dynamic_cast<DerivedB *>(ptrA);

    cout << realA << endl;  // 0x55a4175e0eb0
    cout << realB << endl;  // 0

    return 0;
}

可以看到,将指向 DerivedA 的基类指针用 dynamic_cast 转换成 DerivedA 指针可以成功转换,最后正确的输出了指针的值。而将指向 DerivedA 的基类指针用 dynamic_cast 转换成 DerivedB 指针后,这个指针的值是 0。也就是 dynamic_cast 在进行这种转换的时候会检查这个指针真正指向的类型与要转换成的类型是否相同,如果相同则可以成功转换,否则返回 0。还有要注意的一点是如果想让 dynamic_cast 正确处理这种情况,则这些要转换的类必须具有多态性,说白了就是至少要有一个虚函数。

这也是 dynamic_cast 更安全的原因,如果使用 static_cast 则上面的 realArealB 都将是正确的地址,此时使用 realB 访问一些 DerivedB 独有的内容就会出错。

const_cast<type>(var)

const_cast 用于去除变量的底层const属性。所谓底层const属性表示这个变量所指向的内容是不可变的,而相对的顶层const则表示这个变量自身是不可变的。例如 const int* ptr 是底层const,int* const ptr 是顶层const。

如上所说,const_cast 可以去除变量的底层const属性。

int i = 1;
const int* ptr = &i;
int* ptr1 = const_cast<int *>(ptr);
(*ptr1)++;  // correct

在这个例子中,ptr 指向的变量 i 是可变的,所以可以通过 const_castptrconst 去掉从而修改 i 的值,这样写是合法。但假如 i 本身就是一个 const 变量,则上面的这种用法在语法上还是合法的,只不过最终 i 的值变不变是未定义的。

reinterpret_cast<type>(var)

reinterpret_cast 是最神奇的一种类型转换方式。C++ Primer 对其的解释为:reinterpret_cast 操作符通常为操作数的位模式提供较低层次的重新解释

typedef void(*pfun)();
int func(int i) {
    cout << "Print: " << i << endl;
    return i;
}

int main() {
    pfun ptr = reinterpret_cast<pfun>(func);
    ptr(); 

    return 0;
}

可以看到,一个 int(*)(int) 的函数指针类型被转换成了 void(*)() 类型,还调用成功了!所以 reinterpret_cast 非常变态,C++ Primer 说到,reinterpret_cast 本质上依赖于机器,想要安全的使用 reinterpret_cast 必须对涉及的类型和编译器实现转换的过程都非常了解。

Last modification:November 11th, 2020 at 02:31 pm