C++11新特性:四种类型转换cast说明

引言

  C++11引入了四种类型转换接口,它们分别是static_cast、const_cast、dynamic_cast、reinterpret_cast。
  为什么要使用这四种转换呢?
  给出面向对象编程面向对象设计的五个基本原则,SOLID原则。

  • Single Responsibility Principle:单一职责原则
  • Open Closed Principle:开闭原则
  • Liskov Substitution Principle:里氏替换原则
  • Law of Demeter:迪米特法则
  • Interface Segregation Principle:接口隔离原则
  • Dependence Inversion Principle:依赖倒置原则

  这里不详细叙述五个基本原则,我们使用的cast接口和里氏替换原则有关。

里氏替换原则: “派生类(子类)对象可以在程式中代替其基类(超类)对象。” 以上内容并非利斯科夫的原文,而是译自罗伯特·马丁(Robert Martin)对原文的解读[1]

  解释一下,即是任何基类出现的地方,都可以使用子类去进行替代
比如我们有个基类是鸟:

class birds
{
	public:
	 birds();
	 virtual ~birds(){};
     virtual void Fly() {
        std::cout << "I am  a bird and I am flying" << std::endl;
}

  这时我们派生了两个类,大雁和燕子。

//燕子
class Swallow : public Bird{
public:
    Swallow(){}
    ~Swallow(){}
    void Fly() override {
        std::cout << "I am  a Swallow and I am flying" << std::endl;
    }
};
//大雁
class WildGoose : public Bird{
public:
    WildGoose(){}
    ~WildGoose(){}
    void Fly() override {
        std::cout << "I am  a Wild Goose and I am flying" << std::endl;
    }
};

这时下面的用法是成立的,则是birds& 类型可以被子类替代,则所有基类出现的地方都可以使用子类来进行替换,保证了代码的复用。

//模拟鸟的飞行
void Fly(Bird& b){
    b.Fly();
}

int main(int argc,char * argv[]){
    WildGoose goose;
    Swallow s;
    Fly(s);
    Fly(goose);
}

这时的运行结果为:
在这里插入图片描述
可以看到实现了多态。
如果把主函数使用的fly()函数替换为下列方法:

void Fly(WildGoose& wg){
    wg.Fly();
}

则不能灵活进行复用。

如何规范地遵从里氏替换原则:

  • 1 子类必须完全实现父类的抽象方法,但不能覆盖父类的非抽象方法
  • 2 子类可以实现自己特有的方法
  • 3 当子类覆盖或实现父类的方法时,方法的前置条件(即方法的形参)要比父类方法的输入参数更宽松。
  • 4 当子类的方法实现父类的抽象方法时,方法的后置条件(即方法的返回值)要比父类更严格。
  • 5 子类的实例可以替代任何父类的实例,但反之不成立

1、static_cast

1.1 基本类型转换

1.2 类的上行转换(安全)

  用于子类指针或引用转换为父类指针或引用。

#include <iostream>
using namespace std;

class Base
{
public:
    Base() {};
    virtual void Show() { cout << "This is Base class"; }
};
class Derived :public Base
{
public:
    Derived() {};
    void Show() { cout << "This is Derived class"; }
};
int main()
{
    Derived* der = new Derived;
    auto  Derived = static_cast<Base*> (der);
    //向上转换一直是安全的
    Derived->Show();
    system("pause");
}

输出结果为

This is Derived class

存在虚函数重载,则父类的函数被隐藏不能使用。
由于使用dynamic_caststatic_cast方法会存在开销,则一般使用下列方法进行向上转换。

class Base
{
public:
    Base(){};
    virtual void Show(){cout<<"This is Base class";}
};
class Derived:public Base
{
public:
    Derived(){};
    void Show(){cout<<"This is Derived class";}
};
int main()
{
    Base *base ;
    Derived *der = new Derived;
    //向上转换总是安全
    base = der; 
    base->Show();
    system("pause");
}

1.3 类的下行转换(不安全)

  将父类指针、引用转换为子类指针、引用,但需要程序员自己检查,因此这种转换方式也不存在额外的开销。

2、const_cast

2.1 改变常量属性

  • 常量指针转化为非常量指针;
  • 常量引用转化为非常量引用;
  • 常量对象转化为非常量对象

3、dynamic_cast

  该转换是运行时转换,其余都是编译时转换。主要用于安全的向下进行转换。同时当指针是智能指针时,使用dynamic_cast向下转换不能成功,需使用dynamic_point_cast来进行转换。

3.1 类的上行转换(安全)

此处和static_cast是一样的,不再过多叙述。

#include <iostream>
using namespace std;

class Base
{
public:
    Base() {};
    virtual void Show() { cout << "This is Base calss"; }
};
class Derived :public Base
{
public:
    Derived() {};
    void Show() { cout << "This is Derived class"; }
};
int main()
{
    Derived* der = new Derived;
    auto  Derived = dynamic_cast<Base*> (der);
    //向上转换一直是安全的
    Derived->Show();
    system("pause");
}

3.2 类的下行转换(安全)

  因为有类型检查所以是安全的,但类型检查需要运行时类型信息,这个信息位于虚函数表中,所以必须要有虚函数,否则会转换失败。
在dynamic_cast转换中分为两种情况。

  • 1、当基类指针指向派生对象时能够安全转换。
  • 2、基类指针指向基类时会做检查,转换失败,返回结果0。
#include <iostream>
using namespace std;

class Base
{
public:
    Base() {};
    virtual void Show() { cout << "This is Base class" << endl; }
};
class Derived :public Base
{
public:
    Derived() {};
    void Show() { cout << "This is Derived class" << endl; }
};
int main()
{
    //第一种情况
    Base* base = new Derived;
    Derived* der = dynamic_cast<Derived*>(base);
    //基类指针指向派生类对象时能够安全转换
    der->Show();
    //第二种情况
    Base *base1 = new Base;

    if (Derived* der1 = dynamic_cast<Derived*> (base1))
    {
        der1->Show();
    }
    else
    {
        cout << "error!";
    }
    delete(base);
    delete(base1);
    system("pause");
}
This is Derived class
error!

  引用则和指针不同,指针在C++11中存在空指针,而引用不存在空引用,会引发bad_cast异常。

#include <iostream>
using namespace std;

class Base
{
public:
    Base() {};
    virtual void Show() { cout << "This is Base class" << endl; }
};
class Derived :public Base
{
public:
    Derived() {};
    void Show() { cout << "This is Derived class" << endl; }
};
int main()
{
    //基类引用子类
    Derived b;
    Base& base1 = b;
    Derived& der1 = dynamic_cast<Derived&>(base1);
    der1.Show();

    //基类引用基类
    Base a;
    Base& base2 = a;
    try
    {
        Derived& der2 = dynamic_cast<Derived&>(base2);
    }
    catch(bad_cast)
    {
        cout << "bad_cast error!!" << endl;
    }
    system("pause");
}
This is Derived class
bad_cast error!!

4、reinterpret_cast

4.1 非关联类型的转换

  操作结果是一个指针到其他指针的二进制拷贝,没有类型检查。

5、dynamic_point_cast

转换对象是智能指针时使用dynamic_cast会失败,使用dynamic_point_cast来进行智能指针转换

  • 8
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

面条有点辣

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值