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

2023-11-20

引言

  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来进行智能指针转换

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

C++11新特性:四种类型转换cast说明 的相关文章

  • 调用许多网络服务的最佳方式?

    我有 30 家子公司 每家都实施了他们的 Web 服务 使用不同的技术 我需要实现一个Web服务来聚合它们 例如 所有子公司的Web服务都有一个名为的Web方法GetUserPoint int nationalCode 我需要实现我的网络服
  • 从 Invoke 方法获取 RETURN

    我正在尝试从另一个线程上的列表框项目中读取值 我尝试创建一种新方法来运行调用命令 我可以设法将命令发送到列表框 例如通过调用方法添加 但我似乎无法得到响应 我似乎无法获取该项目的值 我尝试了几种方法 一旦我将它从空变为字符串 事情就开始变得
  • EventHandler 应该始终用于事件吗?

    我一直在愉快地使用自定义委托类型和通用编写事件Action委托类型 没有真正考虑我在做什么 我有一些很好的扩展助手Action and EventHandler这使我倾向于使用那些预定义的委托类型而不是我自己的委托类型 但除此之外 除了惯例
  • 为什么在 C++ 中声明枚举时使用 typedef?

    我已经很多年没有写过任何 C 了 现在我正试图重新开始 然后我遇到了这个并考虑放弃 typedef enum TokenType blah1 0x00000000 blah2 0X01000000 blah3 0X02000000 Toke
  • 在 Mac OS X 上安装 libxml2 时出现问题

    我正在尝试在我的 Mac 操作系统 10 6 4 上安装 libxml2 我实际上正在尝试在 Python 中运行 Scrapy 脚本 这需要我安装 Twisted Zope 现在还需要安装 libxml2 我已经下载了最新版本 2 7 7
  • 如何查明 .exe 是否正在 C++ 中运行?

    给定进程名称 例如 程序 exe C 标准库没有这样的支持 您需要一个操作系统 API 来执行此操作 如果这是 Windows 那么您将使用 CreateToolhelp32Snapshot 然后使用 Process32First 和 Pr
  • DataGridView 列中的数字文本框

    我有一个DataGridView 我想要它的第一列或任何所需的列 其中有textboxes在其中 成为NUMERIC ONLY 我目前正在使用这段代码 private void dataGridViewItems EditingContro
  • 从时间列表中查找最接近的时间

    所以 这是场景 我有一个带有创建时间的文件 我想从该文件的创建时间最接近或相等的时间列表中选择一个时间 完成此操作的最佳方法是什么 var closestTime listOfTimes OrderBy t gt Math Abs t fi
  • C# Winforms Designer 无法打开,因为它无法在同一程序集中找到类型

    我收到以下错误 找不到类型 My Special UserControl 请确保引用包含此类型的程序集 如果此类型是您的开发项目的一部分 请确保已使用当前平台或任何 CPU 的设置成功构建该项目 但没有任何意义的是My Special Us
  • 编写具有多种类型的泛型扩展方法时的类型推断问题

    我正在为 IEnumerable 编写一个通用扩展方法 用于将对象列表映射到另一个映射对象列表 这就是我希望该方法的工作方式 IList
  • 是否可以在Linux上将C转换为asm而不链接libc?

    测试平台为Linux 32位 但也欢迎 Windows 32 位上的某些解决方案 这是一个c代码片段 int a 0 printf d n a 如果我使用 gcc 生成汇编代码 gcc S test c 然后我会得到 movl 0 28 e
  • MSChart 控件中的自定义 X/Y 网格线

    我有一个带有简单 2D 折线图的 C Windows 窗体 我想向其中添加自定义 X 或 Y 轴标记 并绘制自定义网格线 例如 以突出显示的颜色 虚线 我查看了 customLabels 属性 但这似乎覆盖了我仍然想显示的默认网格 这是为了
  • 在 C++ 代码 gdb 中回溯指针

    我在运行 C 应用程序时遇到段错误 在 gdb 中 它显示我的一个指针位置已损坏 但我在应用程序期间创建了 10 万个这样的对象指针 我怎样才能看到导致崩溃的一个 我可以在 bt 命令中执行任何操作来查看该指针的生命周期吗 谢谢 鲁奇 据我
  • 选择 asp.net CheckBoxList 中的所有项目

    ASP NET 和 C 我想要一个带有 全选 项目的复选框列表 当这个特定项目是 已选择 所有其他都将被选择 也 当选择被删除时 这个项目 也将来自所有人 其他物品 选中 取消选中 任何其他项目只会有一个 对特定项目的影响 无论选择状态如何
  • 在 mvc4 中创建通用 mvc 视图

    我以前也提过类似的问题 没有得到答案 如何创建一个通用的 mvc4 视图 该视图可以显示传递给它的模型列表或单个模型 模型可以是个人 组织或团体 无论传递给它的是什么 如果您正在寻找类似的东西 model MyViewModel
  • 如何测试某些代码在 C++ 中无法编译? [复制]

    这个问题在这里已经有答案了 可能的重复 单元测试编译时错误 https stackoverflow com questions 605915 unit test compile time error 我想知道是否可以编写一种单元测试来验证给
  • 用数组或向量实现多维数组

    我想使用单个数组或向量实现多维数组 可以像通常的多维数组一样访问它 例如 a 1 2 3 我陷入困境的是如何实施 操作员 如果数组的维数为 1 则 a 1 应该返回位于索引 1 处的元素 但是如果维数大于一怎么办 对于嵌套向量 例如 3 维
  • 将日期时间显示为 MM/dd/yyyy HH:mm 格式 C#

    在数据库中 日期时间以 MM dd yyyy HH mm ss 格式存储 但是 我想以 MM dd yyyy HH mm 格式显示日期时间 我通过使用 String Format 进行了尝试 txtCampaignStartDate Tex
  • 对多个对象使用事件处理程序

    我有 20 件物品List
  • 运行 xunit 测试时无法将输出打印到控制台窗口

    public class test2InAnotherProject private readonly ITestOutputHelper output public test2InAnotherProject ITestOutputHel

随机推荐

  • 学习PostgreSQL

    参考链接 https www runoob com postgresql postgresql tutorial html
  • Mysql 批量update和批量insert详解

    为了减少与数据库的连接 减少服务器的负荷 需要我们时常对SQL进行分析 优化等操作 针对mysql的批量更新 insert 和 update 就是使用一条INSERT UPDATE语句来更新多条记录 由于不是标准的SQL语法 只能在MySQ
  • 构建知识图谱,让自己更值钱 #CSDN博文精选# #知识图谱# #IT技术#

    大家好 我是小C 又见面啦 文章过滤器 精选大咖干货 助力学习之路 5天20篇CSDN精选博文带你掌握系统化学习方法 专栏将挑选有关 系统化学习方法 的20篇优质文章 帮助大家掌握更加科学的学习方法 在这里 你将收获 快速掌握系统化学习的理
  • Android 弹出通知

    Android 8 0
  • fatal error C1083: 无法打开包含文件:“iostream.h”: No such file or directory

    刚开始用Visual studio net 2003 一个这样的例子 新建了一个win 32项目 include stdafx h include
  • 使用Mask-RCNN在实例分割应用中克服过拟合

    点击上方 AI公园 关注公众号 选择加 星标 或 置顶 作者 Kayo Yin 编译 ronghuaiyang 导读 只使用1349张图像训练Mask RCNN 有代码 代码 https github com kayoyin tiny in
  • 【react】回调函数形式的ref

    回调函数有3个特点 是我定义的函数 我没有调用这个函数 在我没有调用的情况下这个函数自己执行了 ref绑定一个箭头函数作为回调函数 可以输出以下这段看下 ref绑定的箭头函数是会自己执行的 class Demo extends React
  • 关于运发的知识点123(个人笔记 持续更新)

    前言 作为一个物联网的小辣鸡 硬件设计水平不能说没有 只能说一点点 正好要做新项目 自己学着去处理信号 滤波 在这里做一点笔记 参考书一 杨建国老师 新概念模拟电路 pdf 参考书二 单电源运放图集 pdf 原版 翻译中文版 注 想要的自己
  • redis配置 -详情-redis.config

    Redis config 启动的时候 就通过配置文件来启动 单位 配置文件 unit单位 对大小写不敏感 不区分大小写 INCLUDES 包含 include path to local conf include path to other
  • 加糖的CRM---开源项目Sugar CRM中文化安装过程

    目前CRM满天飞 一直也没什么兴趣 好象从几百万美刀到几百块的都有 这次试了试安装Sugar CRM 把安装过程梳理了一下 记下来 以下在环境为windows XP sp2 IIS5 1 MYSQL4 18 PHP5 0下安装成功 首先是要
  • 如何修改NuGet默认全局包文件夹的位置?

    由于一些历史原因 重装系统成为Windows用户解决疑难杂症的祖传手艺 受此影响 给硬盘分区几乎成为了一种执念 少则C D两个盘 夸张一点的5 6个盘的也不是没有 PS macOS和Linux一直都不鼓励给磁盘分区 虽然不禁止但也不提倡 随
  • webpack基础配置

    webpack基础配置 关键字记录 module exports require entry main mode development production output filename path publicPath devServe
  • Git高级之配置多个SSH key

    最近我们在代码托管平台上使用SSH的方式下拉代码 通常是用一个ssh key来拉取所有托管平台的代码 如码云 GitHub GitLab等 但是总用一个不是太好 会有安全风险 这就需要为每个托管平台设置单独的SSH key 下面我们下简单介
  • tensorRT-lenet C++代码分析【附代码】

    前面的文章中已经写了一个tensorRT简单的demo lenet推理 tensorRT lenet 实现了从torch模型转wts 同时也展示出了wts内网络的详细信息 再转engine后的推理过程 本文章是在之前的基础上去分析C 代码的
  • 约瑟夫环生死小游戏-报数下船

    30 个人在一条船上 超载 需要 15 人下船 于是人们排成一队 排队的位置即为他们的编号 报数 从 1 开始 数到 9 的人下船 如此循环 直到船上仅剩 15 人为止 问都有哪些编号的人下船了呢 totalNumber 30 总共人数 n
  • 【网络】传输层-UDP协议

    文章目录 UDP报文格式 UDP协议特性 无连接 不可靠 面向数据报 UDP缓冲区 UDP特性对于编程的影响 基于UDP的应用层协议 netstat工具 pidof命令 UDP报文格式 查看Linux系统下的 usr include net
  • 使用opencv实现简单的人脸识别

    一 opencv模块的使用 1 简介 opencv python是一个python绑定库 旨在解决计算机视觉问题 使用opencv模块 可以实现一些对图片和视频的操作 2 安装 安装opencv之前需要先安装numpy matplotlib
  • kubernetes RC 与 Deployment ,Pod,Horizontal Pod Autoscaling ,replica set资源

    Pod Pod是 kubernetes 的最基本的操作单元 包含一个或多个紧密相关的容器 kubernetes 使用pod在容器之上再封装一层 其一个很重要的原因是 docker容器之间的通信受到docker网络机制的限制 在docker中
  • 华为OD机试真题- Linux发行版的数量【2023Q1】【JAVA、Python、C++】

    题目描述 Linux操作系统有多个发行版 distrowatch com提供了各个发行版的资料 这些发行版互相存在关联 例如Ubuntu基于Debian开发 而Mint又基于Ubuntu开发 那么我们认为Mint同Debian也存在关联 发
  • C++11新特性:四种类型转换cast说明

    目录 引言 1 static cast 1 1 基本类型转换 1 2 类的上行转换 安全 1 3 类的下行转换 不安全 2 const cast 2 1 改变常量属性 3 dynamic cast 3 1 类的上行转换 安全 3 2 类的下