C++ 类型转换

C++ 强制类型转换

C风格转换

对于内置类型而言,C风格转换就很好用,例如:

int i;
double d;
i = (int) d; // i = int(d)

然而,这样的转换符不能应用于类(class)和类的指针。

ANSI-C++标准定义了四个新的转换符,用于控制类(class)之间的类型转换:

  • reinterpret_cast <new_type> (expression);
  • static_cast < type-id > ( expression );
  • dynamic_cast<type *>(source *);;
  • const_cast<type>(expression);

其中,除了dynamic_cast以外的转换,其行为的都是在编译期就得以确定的,转换是否成功,并不依赖被转换的对象。


const_cast

const_cast 主要是用来去除复合类型中const和volatile属性(没有真正去除)。

变量本身的const属性是不能去除的,要想修改变量的值,一般是去除指针(或引用)的const属性,再进行间接修改。

通过const_cast运算符,也只能将const type*转换为type*,将const type&转换为type&,也就是去掉指向const指针或引用的const特性

场景1

先看个例子:

const int constant = 26;
const int* const_p = &constant;
int* modifier = const_cast<int*>(const_p);
*modifier = 3;  // Undefined Behavior 
cout<< "constant: "<<constant<<endl; // 26 
cout<< "constant_p: "<<*const_p<<endl; // 3
cout<<"*modifier: "<<*modifier<<endl; // 3

可见,对声明为const的常量来说,它的值并没有改变(const_cast并不是为了改变常量的值而设计的)。

PS:上面这个例子的用法是借助const_cast修改常量,这是一种未定义的行为,不要这样做!

但是对于指向const的指针如果指向一个非const对象,使用const_cast去掉指针的const属性,借以修改其指向的非const对象是ok的,例如:

int main(int argc, char** argv) {
    int constant = 26; 
    const int* const_p = &constant;
    int* modifier = const_cast<int*>(const_p);
    *modifier = 3;
  
    return 0;
}

场景2

再看一个const_cast应用的例子,如果有一个函数,它的形参是non-const类型变量,而且函数不会对实参的值进行改动,这时我们可以使用类型为const的变量来调用函数,此时const_cast就派上用场了:

void InputInt(int *num)
{
    cout<<*num<<endl;
}

int main()
{
    const int constant = 21;
    //InputInt(&constant); //  invalid conversion from ‘const int*’ to ‘int*’
    InputInt(const_cast<int*>(&constant));
}

场景3

另外一个例子如下,

在const成员函数fun()中的this指针类型为:const student* const this,通过这个this指针无法直接修改数据成员roll;

而const_cast可以将this指针的类型修改为:student* const this,此类型可以修改其所指向的对象;

class student 
{ 
    private: 
        int roll; 
    public: 
        // constructor 
        student(int r):roll(r) {}  

        // A const function that changes roll with the help of const_cast 
        void fun() const { 
            ( const_cast <student*> (this) )->roll = 5;  
        }   

        int getRoll()  { return roll; } 
}; 

int main(int argc, char** argv) {
    student s(3); 
    cout << "Old roll number: " << s.getRoll() << endl; 
  
    s.fun(); 
  
    cout << "New roll number: " << s.getRoll() << endl; 
  
    return 0;
}

场景4

const_cast还可以用来去除volatile 属性,例如

int main(int argc, char** argv) {
    int a1 = 40;
    const volatile int* b1 = &a1; 
    cout << "typeid of b1 " << typeid(b1).name() << '
'; 
    int* c1 = const_cast <int *> (b1); 
    cout << "typeid of c1 " << typeid(c1).name() << '
'; 

    return 0;
}

输出:

typeid of b1 PVKi     // pointer to a volatile and constant integer
typeid of c1 Pi       // Pointer to integer

最后,总结下上面列出了几个使用const_cast的场景:

  • 指向const的指针修改其所指向的非const对象;
  • 将一个const实参传给一个普通形参;
  • const成员函数修改非const成员;
  • 去volatile属性;

一般转换分为两种: 上行转换和下行转换。

上行转换大致意思是把子类实例向上转换为父类型, 下行转换是把父类实例转换为子类实例;

通常子类因为继承关系会包含父类的所有属性, 但是有些子类的属性父类没有;

所以上行转换的时候,子类实例转换给父类是安全的,转换后的指针或者对象可以放心使用父类的所有方法或者属性;

但是下行转换的时候可能是不安全的,因为假如子类有父类没有的属性或者方法的话,父类指针或者实例转换为子类型后,转换后的实例中并没有子类多出来的方法或属性,当调用到这些方法或属性时程序就会崩溃了。


static_cast(静态转换)

编译时的类型转换,例如:

int main(int argc, char** argv) {

    float f = 3.5;
    int a = f; // this is how you do in C 
    int b = static_cast<int>(f);
    cout << a << endl;  // 3
    cout << b << endl;  // 3

    return 0;
}

static_cast会检查类型转换是否合法:

 int a = 10; 
 char c = 'a'; 

 int* q = (int*)&c;              // pass at compile time, may fail at run time 
 int* p = static_cast<int*>(&c); // error!

支持子类指针到父类指针的转换,并根据实际情况调整指针的值,反过来也支持,但会给出编译警告,它作用最类似C风格的“强制转换”,一般来说可认为它是安全的。

class Base {
    public:
        Base(string name) {this->name = name;}
        void f(int p) { cout << "Base::f(int)" << endl; }
        void f(float p) { cout << "Base::f(float)" << endl; }
        virtual void g() { cout << "Base::g()" << endl; }
        virtual void h() { cout << "Base::h() " << name << endl; }
    private:
        string name;
};

class Derived: public Base {
    public:
        Derived(string bname, string name): Base(bname) {this->name = name;}
        void f(int p) { cout << "Derived::f(int)" << endl; }
        virtual void f(float p) { cout << "Derived::f(float)" << endl; }
        void g() { cout << "Derived::g()" << endl; }
        void h1() { cout << "Derived::h1() " << name << endl; }
    private:
        string name;
};

int main(int argc, char** argv) {
    Base b("base");
    Derived d("inherit", "derived");

    Base *pbb = &b;     // ok
    Base *pbd = &d;     // ok
    Base *pbd2 = static_cast<Base*>(&d);    // ok
    pbd2->f(3);         // Base::f(int)
    pbd2->f(3.14f);     // Base::f(float)
    pbd2->g();          // Derived::g()
    pbd2->h();          // Base::h() inherit
    //pbd2->h1();         // error, no member named 'h1' in 'Base'
    

    Derived *pdd = &d;  // ok
    //Derived *pdb = &b;  // error
    Derived *pdb = static_cast<Derived*>(&b);   // unsafe

    Derived *pdb1 = dynamic_cast<Derived*>(&b); // ok, but return NULL
    cout << pdb1 << endl;   // 0x00

    return 0;
}

static_cast主要在以下几种场合中使用:

  1. 用于类层次结构中,父类和子类之间指针和引用的转换;进行上行转换,把子类对象的指针/引用转换为父类指针/引用,这种转换是安全的;进行下行转换,把父类对象的指针/引用转换成子类指针/引用,这种转换是不安全的,需要编写程序时来确认;
  2. 用于基本数据类型之间的转换,例如把int转char,int转enum等,需要编写程序时来确认安全性;
  3. 把void指针转换成目标类型的指针(这是极其不安全的);

dynamic_cast(动态转换)

支持子类指针到父类指针的转换,并根据实际情况调整指针的值,但反过来它就不支持,会导致编译错误,这种转换是最安全的转换;

dynamic_cast 通常用作向下转换【基类到派生类】和平行转换【基类到基类】,运行时,程序会检查source是不是与type类型相兼容,如果是,表达式就返回一个派生类地址,然后转换为type*类型,否则则返回一个NULL。

首先,dynamic_cast依赖于RTTI信息,其次,在转换时,dynamic_cast会检查转换的source对象是否真的可以转换成target类型,这种检查不是语法上的,而是真实情况的检查。
先看RTTI相关部分,通常,许多编译器都是通过vtable(虚函数表)找到对象的RTTI信息的,这也就意味着,如果基类没有虚方法,也就无法判断一个基类指针所指对象的真实类型,这时候,dynamic_cast只能用来做安全的转换,例如从派生类指针转换成基类指针。而这种转换其实并不需要dynamic_cast参与。
也就是说,dynamic_cast是根据RTTI记载的信息来判断类型转换是否合法的。


reinterpret_cast

支持任何转换,但仅仅是如它的名字所描述的那样“重解释”而已,不会对指针的值进行任何调整,用它完全可以做到“指鹿为马”,但很明显,它是最不安全的转换,使用它的时候,你得头脑清醒,知道自己在干什么;

原文地址:https://www.cnblogs.com/chenny7/p/8548941.html