1. static_cast

1.1 static_cast英语的语法

static_cast< new_type >(expression)

备注名称new_type为总体目标基本数据类型,expression为原始记录种类自变量或是关系式。

C设计风格书写:

double scores = 96.5;
int n = (int)scores;

C 新设计风格的书写为:

double scores = 96.5;
int n = static_cast<int>(scores);

1.2 为何要有static_cast等

隐式数据转换是安全性的,显式数据转换是有风险性的,C语言往往提升强制性数据转换的英语的语法,便是为了更好地注重风险性,让程序猿意识到自身在干什么。

可是,这类注重风险性的方法或是较为粗放型,粒度分布较为大,它并沒有说明存有哪些风险性,风险性水平怎样。

为了更好地使潜在性风险性更为优化,使难题追朔更为便捷,使书写格式更为标准,C 对数据转换开展了归类,并增加了四个关键词来给予适用,他们分别是:

关键词 表明
static_cast 用以良好变换,一般不容易造成出现意外产生,风险性很低。
const_cast 用以 const 和非 const、volatile 和非 volatile 中间的变换。
reinterpret_cast 高宽比风险的变换,这类变换只是是对二进制位的再次表述,不容易依靠现有的转换方法对数据信息开展调节,可是能够 完成最灵便的 C 数据转换。
dynamic_cast 依靠 RTTI,用以种类安全性的往下转型发展(Downcasting)。

1.2 static_cast的功效

static_cast等同于传统式的C语言里的强制转换,该操作符把expression变换为new_type种类,用于逼迫隐式变换如non-const目标变为const目标,编译程序时查验,用以非多态的变换,能够 变换表针以及他,但沒有运作时种类查验来确保变换的安全系数。它关键有以下几类使用方法:

风险性较低的使用方法:

  • 原来的全自动数据转换,比如 short 转 int、int 转 double、const 转非 const、往上转型发展等;
  • void 表针和实际种类表针中间的变换,比如void *int *char *void *等;
  • 有变换构造方法或是数据转换涵数的类与其他种类中间的变换,比如 double 转 Complex(启用变换构造方法)、Complex 转 double(启用数据转换涵数)。

必须 留意的是,static_cast 不可以用以不相干种类中间的变换,由于这种变换全是有风险性的,比如:

  • 2个实际种类表针中间的变换,比如int *double *Student *int *等。不一样种类的数据储存文件格式不一样,长短也不一样,用 A 种类的表针偏向 B 种类的数据信息后,会依照 A 种类的方法来解决数据信息:如果是载入实际操作,很有可能会获得一堆没有意义的值;如果是载入实际操作,很有可能会使 B 种类的数据信息遭受毁坏,当再度以 B 种类的方法获取数据的时候会获得一堆没有意义的值。
  • int 和表针中间的变换。将一个实际的详细地址取值给指针变量是十分风险的,由于该详细地址上的运行内存很有可能沒有分派,也很有可能沒有读写能力管理权限,正好是可用内存反倒是偶然性。

1.3 static_cast使用方法

#include <iostream>
#include <cstdlib>
using namespace std;
class Complex{
public:
    Complex(double real = 0.0, double imag = 0.0): m_real(real), m_imag(imag){ }
public:
    operator double() const { return m_real; }  //数据转换涵数
private:
    double m_real;
    double m_imag;
};
int main(){
    //下边是恰当的使用方法
    int m = 100;
    Complex c(12.5, 23.8);
    long n = static_cast<long>(m);  //宽变换,沒有信息内容遗失
    char ch = static_cast<char>(m);  //窄变换,很有可能会遗失信息内容
    int *p1 = static_cast<int*>( malloc(10 * sizeof(int)) );  //将void表针变换为实际种类表针
    void *p2 = static_cast<void*>(p1);  //将实际种类表针,变换为void表针
    double real= static_cast<double>(c);  //启用数据转换涵数
    //下边的使用方法是不正确的
    float *p3 = static_cast<float*>(p1);  //不可以在2个实际种类的表针中间开展变换
    p3 = static_cast<float*>(0X2DF9);  //不可以将整数金额变换为表针种类
    return 0;
}

2. dynamic_cast

2.1 dynamic_cast 英语的语法

dynamic_cast <newType> (expression)

newType 和 expression 务必与此同时是表针种类或是引用类型。也就是说,dynamic_cast 只有变换表针种类和引用类型,其他种类(int、double、二维数组、类、建筑结构等)都不好。

针对表针,假如变换不成功将回到 NULL;针对引入,假如变换不成功将抛出去std::bad_cast出现异常。

2.2 dynamic_cast 使用方法

dynamic_cast 用以在类的承继层级中间开展数据转换,它既容许往上转型发展(Upcasting),也容许往下转型发展(Downcasting)。往上转型发展是没有理由的,不容易开展一切检验,因此都能取得成功;往下转型发展的前提条件务必是安全性的,要依靠 RTTI 开展检验,全部仅有一部分能取得成功。

dynamic_cast 与 static_cast 是相对性的,dynamic_cast 是“动态性变换”的含意,static_cast 是“静态数据变换”的含意。dynamic_cast 会在程序执行期内依靠 RTTI 开展数据转换,这就规定基类务必包括虚函数;static_cast 在编译程序期内进行数据转换,可以更为立即地出现未知错误。

2.3 dynamic_cast 案例

2.3.1 往上转型发展(Upcasting)

往上转型发展时,只需待变换的2个种类中间存有承继关联,而且基类包括了虚函数(这种信息内容在编译程序期内就能明确),就一定能变换取得成功。由于往上转型发展自始至终是安全性的,因此 dynamic_cast 不容易开展一切运作期内的查验,这个时候的 dynamic_cast 和 static_cast 就没什么差别了。

往上转型发展时不实行运作期检验」尽管提升 了高效率,但也留有了安全风险,请看下面的代码:

#include <iostream>
#include <iomanip>
using namespace std;
class Base{
public:
    Base(int a = 0): m_a(a){ }
    int get_a() const{ return m_a; }
    virtual void func() const { }
protected:
    int m_a;
};

class Derived: public Base{
public:
    Derived(int a = 0, int b = 0): Base(a), m_b(b){ }
    int get_b() const { return m_b; }
private:
    int m_b;
};

int main(){
    //状况①
    Derived *pd1 = new Derived(35, 78);
    Base *pb1 = dynamic_cast<Derived*>(pd1);
    cout<<"pd1 = "<<pd1<<", pb1 = "<<pb1<<endl;
    cout<<pb1->get_a()<<endl;
    pb1->func();
    //状况②
    int n = 100;
    Derived *pd2 = reinterpret_cast<Derived*>(&n);
    Base *pb2 = dynamic_cast<Base*>(pd2);
    cout<<"pd2 = "<<pd2<<", pb2 = "<<pb2<<endl;
    cout<<pb2->get_a()<<endl;  //輸出一个废弃物值
    pb2->func();  //内存错误
    return 0;
}

运作結果以下

image-20210724114624100

能够 见到pd1与pb1的详细地址同样,且pb1能够 一切正常启用Base类的方式

针对状况②

pd 2偏向的是整型变量 n,并沒有偏向一个 Derived 类的目标,在应用 dynamic_cast 开展数据转换时都没有查验这一点由于往上转型发展自始至终是安全性的,因此 dynamic_cast 不容易开展一切运作期内的查验

只是将 pd 的值立即赋给了 pb(这儿并不一定调节偏移),最后造成 pb 也偏向了 n。由于 pb 偏向的并不是一个目标,因此get_a()无法得到 m_a 的值(事实上获得的是一个废弃物值),pb2->func()无法得到 func() 涵数的恰当详细地址

运作結果以下

image-20210724115237948

image-20210724115309768

简易而言便是往上转型发展不是查验的,因此大伙儿得了解自身在干什么,不可以随便的变换

2.3.2 往下转型发展(Downcasting)

往下转型发展是有风险性的,dynamic_cast 会依靠 RTTI 信息内容开展检验,明确安全性的才可以变换取得成功,不然就变换不成功。

下边看一个事例

#include <iostream>
using namespace std;
class A{
public:
    virtual void func() const { cout<<"Class A"<<endl; }
private:
    int m_a;
};
class B: public A{
public:
    virtual void func() const { cout<<"Class B"<<endl; }
private:
    int m_b;
};
class C: public B{
public:
    virtual void func() const { cout<<"Class C"<<endl; }
private:
    int m_c;
};
class D: public C{
public:
    virtual void func() const { cout<<"Class D"<<endl; }
private:
    int m_d;
};
int main(){
    A *pa = new A();
    B *pb;
    C *pc;
    //状况①
    pb = dynamic_cast<B*>(pa);  //往下转型发展不成功
    if(pb == NULL){
        cout<<"Downcasting failed: A* to B*"<<endl;
    }else{
        cout<<"Downcasting successfully: A* to B*"<<endl;
        pb -> func();
    }
    pc = dynamic_cast<C*>(pa);  //往下转型发展不成功
    if(pc == NULL){
        cout<<"Downcasting failed: A* to C*"<<endl;
    }else{
        cout<<"Downcasting successfully: A* to C*"<<endl;
        pc -> func();
    }
    cout<<"-------------------------"<<endl;
    //状况②
    pa = new D();  //往上转型发展全是容许的
    pb = dynamic_cast<B*>(pa);  //往下转型发展取得成功
    if(pb == NULL){
        cout<<"Downcasting failed: A* to B*"<<endl;
    }else{
        cout<<"Downcasting successfully: A* to B*"<<endl;
        pb -> func();
    }
    pc = dynamic_cast<C*>(pa);  //往下转型发展取得成功
    if(pc == NULL){
        cout<<"Downcasting failed: A* to C*"<<endl;
    }else{
        cout<<"Downcasting successfully: A* to C*"<<endl;
        pc -> func();
    }
    return 0;
}

运作結果

image-20210724120141187

能够 见到,前2次变换不成功,可是后2次变换取得成功

这一段编码中类的继承顺序为:A --> B --> C --> D。pa 是A*种类的表针,当 pa 偏向 A 种类的目标时,往下转型发展不成功,pa 不可以变换为B*C*种类。当 pa 偏向 D 种类的目标时,往下转型发展取得成功,pa 能够 变换为B*C*种类。一样全是往下转型发展,为何 pa 偏向的目标不一样,变换的結果就截然不同呢?

由于每一个类都是会在运行内存中储存一份类型信息,c语言编译器会将存有承继关联的类的类型信息应用表针“联接”起來,进而产生一个承继链(Inheritance Chain),也就是如下图所显示的模样:

image-20210724120354068

当应用 dynamic_cast 对表针开展数据转换时,程序流程会先寻找该表针偏向的目标,再依据目标寻找当今类(表针偏向的目标隶属的类)的类型信息,并此后连接点逐渐顺着承继链往上解析xml,假如找到要转换的总体目标种类,那麼表明这类变换是安全性的,就可以变换取得成功,要是没有寻找要变换的总体目标种类,那麼表明这类变换存有很大的风险性,就不可以变换。

因此在第二种方法中,pa事实上是偏向的D,因此程序流程沿着D逐渐往上找,找到B和C,因此评定是安全性的,因此变换取得成功

总起来说,dynamic_cast 会在程序执行全过程中解析xml承继链,假如中途碰到了要变换的总体目标种类,那麼就可以变换取得成功,假如直至承继链的端点(最高层的基类)都还没碰到要变换的总体目标种类,那麼就变换不成功。针对同一个表针(比如 pa),它偏向的目标不一样,会造成解析xml承继链的起始点不一样,中途可以配对到的种类也不一样,因此同样的数据转换造成了不一样的結果。

3. 参照连接

http://c.biancheng.net/cpp/biancheng/view/3297.html

https://blog.csdn.net/u014624623/article/details/79837849
https://www.cnblogs.com/wanghongyang/ 【文中blog】

评论(0条)

刀客源码 游客评论