一篇文章比较static_cast和dynamic_cast的区别

static_cast和dynamic_cast的区别如下:

    1. 基本类型
      • 1.1 基本类型间转换:
        • static_cast可以在基本类型间做转换,与隐式转换规则相同,不符合隐式转换规则的static_cast同样不能转换。
        • dynamic_cast不能再基本类型之间做转换。
      • 1.2 基本类型指针或引用间转换
        • static_cast不能在基本类型指针或者引用间做转换
        • dynamic_cast不能在基本类型指针或引用间做转换
      • 1.3 基本类型指针与void *之间做转换
        • static_cast可以在基本类型指针与void *之间做转换
        • dynamic_cast不能在基本类型指针与void*之间做转换
  • 2.类继承之间转换

    • 2.1 无关类之间互相转换
      • static_cast不能在没有继承关系的类指针或引用之间做转换。
      • dynamic_cast也不能够在没有继承关系的类指针或引用之间做转换,如果在没有继承关系的类之间做转换,那么转换后的指针为空。虽然偶尔程序不会崩溃,但是这种是不推荐的也是不对的。dynamic_cast的转化有个前提,即被转换的类必须有虚函数表,否则会报错error: cannot dynamic_cast ‘ps’ (of type ‘class Son*’) to type ‘class Father*’ (source type is not polymorphic),其中Son和Father为用户自定义的类,没有继承关系,Son没有virtual函数,即没有虚函数表。如果在Son类中声明一个virtual函数,即Son类具备了虚函数表,则不会报此错误了,虽然转换后的指针是NULL。所以说dynamic_cast需要在有虚函数表的类上使用,否则会报错。
      • dynamic_cast在极特殊的情况下可以在无关类之间做转换,但是在实际编写代码做工程的时候一定不能这么做,而且要在转换后判断转换后的指针是否为空,如果转换失败那么指针为空。总之,dynamic_cast的常规用法还是在具有继承关系的类之间做转换的。
#include <iostream>
#include <stdlib.h>
#include <stdio.h>
using namespace std;

class Father {
public:
    void fun() { cout << "this is father fun" << endl; }
    //void fun1() { cout << "fun1" << endl; }
};

class Son {
public:
    virtual void fun() { cout << "this is son fun" << endl; }
    //void fun2() { cout << "fun2" << endl; }
    int k;
};

void test() {
    Father * pf, f;
    Son * ps, s;

    pf = &f;
    ps = &s;
    pf = dynamic_cast<Father *>(ps);
    printf("%p  %p  %p\n",ps,pf,NULL);
    pf->fun();
    //pf->fun1();
}

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

上面这个例子能够正确编译和执行,执行结果如下:

[root@VM_24_16_centos cast]# ./a.out 
0x7fff9e261230  (nil)  (nil)
this is father fun
fun1
为什么指针打印为空但是还是执行对了程序没有崩溃???答案是:原因还没有搞清楚,但是确定的是这是瞎猫撞上死耗子了,如果fun()引用到了成员变量,那么程序指定会崩溃,怀疑没有崩溃的原因是在没有使用到数据成员的时候,只是需要通过类名找到函数成员,然后执行,虽然指针为空,但是此时只是通过类名进行查找,并没有使用到此指针;但是一旦需要引用成员变量的时候就需要使用这个指针查找成员变量的位置了,但是因为转换后指针为空,所以导致程序崩溃。
例如如下稍微修改之后的程序:
#include <iostream>
#include <stdlib.h>
#include <stdio.h>
using namespace std;

class Father {
public:
    void fun() {
        k = 1;
        cout << "this is father fun" << endl; }
private:
    int k;
};

class Son {
public:
    virtual void fun() { cout << "this is son fun" << endl; }
    int k;
};

void test() {
    Father * pf, f;
    Son * ps, s;

    pf = &f;
    ps = &s;
    pf = dynamic_cast<Father *>(ps);
    if(pf == NULL) {
        cout << "pf is NULL" << endl;
    }
    printf("%p  %p  %p\n",ps,pf,NULL);
    pf->fun();
}

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

此程序在执行的时候就发生了崩溃,

[root@VM_24_16_centos cast]# g++ cast1.cpp 
[root@VM_24_16_centos cast]# ./a.out 
pf is NULL
0x7ffdc7ad9a20  (nil)  (nil)
Segmentation fault
[root@VM_24_16_centos cast]# 

所以,这个故事告诉我们一个道理!!!
在使用dynamic_cast的时候需要判断转换后的指针是否为空,如果为空说明转换时不允许的,只有不为空才能继续运行程序

 - 2.2 子类型转换成父类型
     - static_cast与dynamic_cast的作用相同。
     - dynamic_cast转换后的指针此时就不为空了。


 - 2.3 父类型转换为子类型
     - static_cast可以把父类型指针转换为子类型指针,但是由于没有静态类型检查,所以是不安全的。那么何时可以使用static_cast把父类型指针转换成子类型指针?答案是在实际使用的时候这种强制类型转换一般都是用在参数传递中,在传递过程中需要确保传递过来的类型与转换之后的类型相同,虽然在传递的时候形参的类型不同,但是其实形参指向的真正类型是一样的,及形参类型虽然是Father,但是实际指向的是Son,转换完成之后还是Son,这种类型一直需要程序员来保证,但是dynamic_cast能够自动检查这种类型是否一致。
#include <iostream>
#include <stdlib.h>
#include <stdio.h>
using namespace std;

class Father {
public:
    void fun() {
        cout << "this is father fun" << endl;
    }
    void fun1() {
        cout << "this is father fun1" << endl;
    }
};

class Son : public Father {
public:
    void fun() { cout << "this is son fun" << endl; }
    void fun2() { k[999] = 2;n[999] = 3;cout << "this is son fun2" << endl; }
private:
    int m[1000];
    int n[1000];
    int k[1000];
};

void test() {
    Father * pf, f;
    Son * ps, s;

    pf = &f;
    ps = &s;
    ps = static_cast<Son *>(pf);
    if(ps == NULL) {
        cout << "ps is NULL" << endl;
    }
    printf("%p  %p  %p\n",ps,pf,NULL);
    ps->fun2();
    pf->fun();
}

int main(int argc, char ** argv) {
    test();
    return 0;
}
  • dynamic_cast也可以把子类型转换为父类型,但是在转换的时候是有运行时类型检查的,如果转换失败会返回NULL,因此在使用的时候最好判断返回值。例如:
#include <iostream>
#include <stdlib.h>
#include <stdio.h>
using namespace std;

class Father {
public:
    virtual void fun() {
        cout << "this is father fun" << endl;
    }
};

class Son : public Father {
public:
    void fun() { cout << "this is son fun" << endl; }
};

void test() {
    Father * pf, f;
    Son * ps, s,s1;

    pf = &f;
    //pf = &s1;
    ps = &s;
    ps = dynamic_cast<Son *>(pf);
    if(ps == NULL) {
        cout << "ps is NULL" << endl;
    }
    printf("%p  %p  %p\n",ps,pf,NULL);
    pf->fun();
}

int main(int argc, char ** argv) {
    test();
    return 0;
}
     此代码转换失败,返回NULL,原因是pf在转换之前指向的类型为Fathre与要转换的类型Son不符合,所以转换失败。
     如果把//pf = &s1;加上的话,那么转换成功,原因是pf转换前后的运行时类型相同。

(所以这个结论非常重要:如果被转换的指针在转换前后的运行时类型(即运行时候的真实类型)相同,那么转换成功,如果运行时类型不同,那么转换失败。)
如下例子:

#include <iostream>
#include <stdlib.h>
#include <stdio.h>
using namespace std;

class Father {
public:
    virtual void fun() {
        cout << "this is father fun" << endl;
    }
};

class Son : public Father {
public:
    void fun() { cout << "this is son fun" << endl; }
};

void test() {
    Father * pf, f;
    Son * ps, s,s1;

    pf = &f;
    pf = &s1;
    ps = &s;
    ps = dynamic_cast<Son *>(pf);
    if(ps == NULL) {
        cout << "ps is NULL" << endl;
    }
    printf("%p  %p  %p\n",ps,pf,NULL);
    pf->fun();
}

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

父类指针在转换前就指向Son类型对象,转换的类型也是Son类型,那么类型匹配,可以转换成功。
下面是一个参数转换的例子:

#include <iostream>
#include <stdlib.h>
#include <stdio.h>
using namespace std;

class Father {
public:
    virtual void fun() {
        cout << "this is father fun" << endl;
    }
};

class Son : public Father {
public:
    void fun() { cout << "this is son fun" << endl; }
};

void test(Father * pf) {
    Son * ps, s;
    ps = &s;
    ps = dynamic_cast<Son *>(pf);
    if(ps == NULL) {
        cout << "ps is NULL" << endl;
    }
    printf("%p  %p  %p\n",ps,pf,NULL);
    pf->fun();
}

int main(int argc, char ** argv) {
    Father * p = new Father();
    //p = new Son();
    test(p);
    return 0;
}

转换失败,因为pf转换前后运行时类型不相同,pf指针转换后为空。
如果把 //p = new Son(); 加上,那么转换成功,pf指针不为空。

所以最终的结论是:
static_cast转换不做运行时的类型检查,转换正确性由程序员负责。
dynamic_cast转换时会做运行时类型检查,只有转换前后的运行时类型相同,才能转换成功,否则转换失败,转换后的指针为NULL
static_cast和dynamic_cast这些强制类型转换一般用在类似上述例子的参数传递过程中。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值