c++之四种类型转换

const_cast

  1. const_cast的主要用途是去掉对象引用或指针的const属性。但是即使转换后,对原始的const对象也不能真正的改变其值,只能是改变其引用或者*指针的值(具体如何实现没有深入研究,参考下面的代码)。
  2. const_cast的使用场景不在于要修改const对象的值,而是主要用在参数传递等场景。参考代码中的注释。
#include <iostream>

using namespace std;

void test_const_cast(void) {
        //const_cast转换指针,去掉const属性
        const int a = 10;
        const int * p = &a;
        int * p1 = const_cast<int *>(p);
        *p1 = 20;
        cout << a << "  " << *p << "  " << *p1 << endl; //输出 10  20  20,修改并没有改变原来const对象的值
        cout << &a << "  " << p << "  " << p1 << endl;  //输出 0x7fff12d2b0bc  0x7fff12d2b0bc  0x7fff12d2b0bc,虽然地址一样,但是值不一样。
                                                        //没有深入追究,但是总之虽然转成了非const的指针或者引用,但是原来const对象的值并没有被改变。
                                                                //const_cast转换引用,去掉const属性
        const int & ref = a;
        int & ref1 = const_cast<int &>(ref);
        ref1 = 30;
        cout << a << "  " << ref << "  " << ref1 << endl; //使用引用也是如此,原来const对象的值并不会被改变。

        //const_cast转换的原始对象为非const
        int b = 7;
        const int * bp = &b;
        int * bp1 = const_cast<int *>(bp);
        *bp1 = 14;
        cout << b << "  " << *bp << "  " << *bp1 << endl; //与上例不同,此处真正修改了原始变量的值。输出 14 14 14
/*
 * 既然const_cast<T>()转换之后也不能修改原来const对象的值,那么为什么还要有const_cast呢?
 * 有两个使用场景:
 *      1. 如果一个函数的参数是非const的,而且在函数内部并不会修改这个非const对象的值,现在想要传递一个const对象给这个函数,那么这个时候就需要首先使用
 * const_cast转换去掉这个const对象的const属性。
 *      2. 如果把一个非const对象赋值给了const指针或者引用,在后续的使用当中又需要去掉这个const属性,那么可以采用const_cast的方法。
 * 而且这种情况使用const_cast转换后修改变量的值会使得原始的变量真正的被修改了。
 *      此种变成方法不提倡,const指针或者引用不应该转换成非const,如果需要修改的话可以采用别的方式,例如重新定义指针,这样才能使得程序容易理解。
 */
}

int main(int argc, char ** argv) {
        test_const_cast();
        return 0;
}

static_cast

  1. static_cast和dynamic_cast的验证都记载在如下的代码中,代码的注释为验证的结论。在最后会有一个static_cast和dynamic_cast的不同的说明。
    • 代码如下:
#include <iostream>

using namespace std;

class A {
};

class B {
};

class A1 : public A {
};

class A2 : public A {
};

class C {
public:
    C():a(10) {}
    virtual ~C(){}
    void getType( void ) { cout << "The class is C" << endl; }
    void testC( void ) { cout << "C" << endl; }
    void intC( void ) { cout << a << endl; }
private:
    int a;
};

class C1 : public C {
};

class D {
public:
    virtual ~D(){}
    void getType( void ) { cout << "The class is D" << endl; }
    void testD( void ) { cout << "D" << endl; }
};

void test(void) {
    //1.static_cast在基本类型之间做转换,static_cast可以用于执行任何隐式转换,包括标准转换和用户定义的转换。
    //    static_cast的转换需要程序员保证转换的安全性。
    char a = 'a';
    int vInt = static_cast<int>(a);
    cout << a << "  " << vInt << endl;

    //2.static_cast在指针之间做转换
    //   不能在基本类型的指针之间通过static_cast做转换
    //   可以在void *指针和指定指针类型之间做转换
    //   可以在派生类和基类之间做转换,但是不能在相同基类的派生类之间做转换,也不能在无关的类指针或者引用之间做转换。
    //   除了dynamic_cast,其余的cast都是在编译的时候做的转换,dynamic_cast的转换是在运行时做的转换
    //   static_cast不能转换掉expression的const、volatile和__unaligned属性
    char b = 'b';
    char * pChar = &b;
    //int * pTmp = static_cast<int *>(pChar); //error: 错误:从类型‘char*’到类型‘int*’中的 static_cast 无效
    void * pVoid = &b;
    char * pChar1 = static_cast<char *>(pVoid); //char *转为void *
    void * pVoid1 = static_cast<void *>(pChar1); //void *转为char *
    cout << b << "  " << *(char *)pVoid << "  " << *pChar1 << "  " << *(char *)pVoid1<< endl;
    A * pA = new A();
    B * pB = new B();
    A1 * pA1 = new A1();
    //B * pChange = static_cast<B *>(pA); //错误:从类型‘A*’到类型‘B*’中的 static_cast 无效
                                        //static_cast进行不相关的类之间的转换会报错。
    A * pAChange = static_cast<A *>(pA1); //把子类型指针转换为基类型指针
    A1 * pA1Change = static_cast<A1 *>(pA); //把基类型指针转换为子类型指针
    //A2 * pA2Change = static_cast<A2 *>(pA1Change); //子类之间进行转换,错误:从类型‘A1*’到类型‘A2*’中的 static_cast 无效

    //3. dynamic_cast在指针之间做转换
    //    不存在虚表的类不能互相转换
    //A * pAChange1 = dynamic_cast<A *>(pB); //错误:无法将‘pB’从类型‘B*’动态转换到类型‘class A*’(源类型不是多态的)
    C * pC = new C();
    D * pD = new D();
    C * pCChange1 = static_cast<C *>(pD); //错误:从类型‘D*’到类型‘C*’中的 static_cast 无效
    C * pCChange = dynamic_cast<C *>(pD); //存在虚表的类,即使类之间没有继承关系也可以转换。
    //从上面两行代码中可以看出,static_cast在转换的时候是会检查继承关系的,无关类之间不能转换,而dynamic_cast在转换的时候是不会检查类关系的,无关的类可以互相转换
    //因此转换的正确与否需要程序员来保证。这是两者的区别。
    pD->getType();
    pCChange->getType(); //转换前后的类共有的函数肯定可以正常执行。
    pCChange->testC();//如果强制转换后的类的特有函数中没有需要访问成员变量的(私有内存)的操作,那么函数能够被正确执行,不会报错。
    pCChange->intC(); //出现段错误,所以dynamic_cast需要程序员来保证转换正确性。
 // http://www.cnblogs.com/bastard/archive/2011/12/14/2288117.html
 // http://blog.csdn.net/wingfiring/article/details/633033
}

int main(int argc, char ** argv) {
    test();
    return 0;
}

dynamic_cast

  1. 除了dynamic_cast以外的转换,其余的3种类型转换操作符的行为的都是在编译期就得以确定的,转换是否成功,并不依赖被转换的对象。而dynamic_cast则不然.
  2. 首先,dynamic_cast依赖于RTTI信息.
  3. 其次,在转换时,dynamic_cast会检查转换的source对象是否真的可以转换成target类型,这种检查不是语法上的,而是真实情况的检查。先看RTTI相关部分,通常,许多编译器都是通过vtable找到对象的RTTI信息的,这也就意味着,如果基类没有虚方法,也就无法判断一个基类指针变量所指对象的真实类型, 这时候,dynamic_cast只能用来做安全的转换,例如从派生类指针转换成基类指针.而这种转换其实并不需要dynamic_cast参与.也就是说,dynamic_cast是根据RTTI记载的信息来判断类型转换是否合法的.
  4. 从上面的static_cast的代码验证中可以看出来,static_cast在无关类之间的转换会报错;而dynamic_cast在无关类之间的转换不会报错。dynamic_cast的转换需要转换类好被转换类都有虚函数表。如果没有虚函数表,那么只能执行安全转换了,即从派生类转换成基类。
  5. 虽然dynamic_cast能在无关类之间进行转换,但是需要注意转换后的类型安全性需要程序员自己来验证,比如说调用转换前后的类共有的函数,那么能够调用到,但是如果两个类对这个函数的实现不同,比如转换前的类的实现中调用了私有成员变量b,但是转换后的类中的这个函数调用了私有成员变量a,因为转换前的类中没有成员变量a,那么转换后调用成员变量a肯定会出错,导致程序崩溃,具体参考实验代码。

static_cast和dynamic_cast的区别是什么

  • 答案是:
    1. static_cast可以在基本类型之间做转换(即支持原来所有的隐式类型转换),不允许在基本类型指针之间做转换,不允许在无关类之间做转换,可以在void 和基本类型指针间做转换;dynamic_cast也不允许在无关类之间做转换,如果在无关类之间做转换那么转换后指针为NULL,dynamic也不允许在基本类型及基本类型指针或引用还有void 之间转换。
    2. dynamic_cast的被转换类必须有虚函数表。
    3. dynamic_cast被转换的指针的运行时类型必须与转换的目的类型相同,否则转换失败。
    4. static_cast和dynamic_cast都可以用在基类和派生类之间的转换,在派生类转换成基类的时候两者是相同的;在基类转换成派生的时候,static_cast的类型一致性要程序员自己保证,即程序员要自己保证转换前基类指针指向的类型与转换之后的派生类类型一致;而dynamic_cast的类型一致性可以指针自己来保证,即如果转换前后的运行时类型不一致,那么转换失败,转换后指针为NULL(所以如果使用dynamic_cast从基类指针转换到派生类指针,要想成功,那么转换的时候基类指针的运行时类型必须与要转换的派生类类型一致,否则转换失败,返回null).
    5. 这些强制转换一般用在参数传递过程中。

reinterpret_cast

  • reinterpret_cast是对指针指向的内存位置的bit位做重新解释,可以转换任何类型的。

总结:

  • C++的四种类型转换中,const_cast和reinterpret_cast的功能比较明确。容易混淆的就是static_cast和dynamic_cast的使用。通过上文的学习与总结,弄懂了两者的不同与使用场景。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值