zoukankan      html  css  js  c++  java
  • 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

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

  • 相关阅读:
    精彩分享
    Cocoa.Programming.for.Mac.OS.X 3rd 前8章小知识点
    ObjectiveC protocol 和 delegate
    Object copying
    KeyValue Observing
    实现KeyValue Coding 的5种方法
    关于 XIB 和 NIB
    ObjectiveC 中的 self 和 super
    使用KeyValue Coding
    PrepareStatement的两个executeQuery语句
  • 原文地址:https://www.cnblogs.com/chenny7/p/8548941.html
Copyright © 2011-2022 走看看