C++primer(第五版)第十八章(用于大型程序的工具)

news2024/11/22 18:44:03

不做大项目的话估计下面的都暂时用不到,包括下一章

大规模应用程序要求:能使用各种库进行协调开发(多人多文件编程);能在独立开发的子系统之间协同处理错误(说人话就是我用了你写的库结果报错了,我们得协调处理好出错的地方);能对比较复杂的应用概念进行建模(定义合理的类,函数以及模板).

18.1 异常处理

异常处理机制允许程序中独立开发的部分能在运行时对于出现的问题进行通信并做出相应的处理.

18.1.1抛出异常

我们可以手动抛出(throw)一条表达式来引发异常.

执行throw时,throw后面的语句将不再被执行,程序的控制器从throw转移到与之匹配的catch模块.

抛出异常后,程序将会暂停并寻找与之匹配的catch子句.

当throw出现在try语句块(大多数情况都是这样),检查与之相匹配的catch则执行对应的代码块,找不到则向外层的try中寻找(若try没有被try嵌套,则持续在外层函数寻找).若一直找不到则退出主函数然后查找终止.

即一个异常若是没有被捕获,就会终止当前程序(所以有时候我们的代码即使报错也不会终止程序就是因为有异常捕获的异常处理机制).

类对象分配的资源将由类的析构函数负责释放,因此如果我们使用类来控制资源的分配,就能确保无论函数正常结束还是遭遇异常,资源都能被正确地释放.(这段话我不是很理解,所以把书里的原话放上来了.如果感兴趣的同学可以去翻阅原书,能够理解的话可以在评论区教我).总之就是析构函数不会因为异常而不执行.所以我们必须确保在析构函数里不能抛出捕获不了的异常,如果可能会抛出异常,而需要做好对应的异常处理机制.

异常对象是一种特殊的对象,编译器通过异常抛出表达式来对异常对象进行拷贝初始化,因此抛出(throw)的表达式必须是完全类型(声明并定义),如果该表达式是一个类类型,则需要有可访问的析构函数和可访问的拷贝构造函数或是移动构造函数.抛出一个指向局部对象的指针几乎是错误的行为.

18.1.2 捕获异常

 捕获异常的基本格式如下,可以有多个catch语句,并且try中也可以嵌套try和catch.(但是建议不要写那么复杂)

try{
    //可能抛出异常的代码块.
}catch( 异常对象1 ){
    //若是try中抛出了异常对象1则执行这里的代码.
    //也可以在这里接着将异常抛出.
    throw ...
}catch( 异常对象2 ){

}catch( ... ){
    //若是在catch后面的括号上写着 ... 则表示捕获所有异常,并且必须在所有catch的最后
}

catch后面的括号为异常声明,形如函数形参列表.通常情况下,如果catch接收的异常与某个继承体系有关,则最好将catch接收的异常定义为引用类型.需要将越专门越精细的catch放在最后(有可能发生类型转换),将捕获所有异常的catch放在最后.

异常与catch异常声明的匹配规则将会较为严格,绝大多数的类型转换不被允许,但允许

1,从非常量向常量转换

2,派生类向基类转换(子类变父类)

3,数组被转换成指向数组的指针

18.1.3 函数try语句块与构造函数

若要处理构造函数初始值抛出的异常,需要将构造函数写成函数try语句块(函数测试块)的形式,例如:

MyClass(int age) try:{
    
}catch( ... ){

}

18.1.4 noexcept异常说明

C++11新标准中,可以使用关键字noexcept说明指定某个函数不会抛出异常.noexcept紧跟在参数列表后,例如:

//函数声明
void fun(int a) noexcept;

但声明归声明,大部分编译器不会检查noexcept,即使你在使用了noexcept的函数中仍然抛出了异常编译器也不会警告.

所以noexcept只在以下两种情况下使用:1,我们确定函数不会抛出异常.2,我们不知道如果处理异常.

noexcept接收一个bool的参数,若为true,则表示确定不会抛出异常,为false则表示可能还是会抛出异常.以此可以使用某个变量值来判断是否要用noexcept标记某函数.

18.1.5 异常类层次

下图为标准 exception 类层次

exception定义了拷贝构造函数,拷贝赋值运算符,虚析构函数和一个名为that的虚成员(下面会介绍虚的概念).what函数返回一个const char* ,该指针指向一个以null结尾的字符数组,以此确保不会抛出任何异常.

exception,bad_cast,bad_alloc定义了默认构造函数.而runtime_error和logic_error没有默认构造函数,但是有一个接收C风格字符串或者标准库string类型实参的构造函数,实参负责提供关于错误的更多信息.

我们可以通过继承上列异常的方式来自定义异常.例如:

class my_error: public std::runtime_error{ //继承自runtime_error
public:
    explicit my_error(const std::string &s):std::runtime_error(s) { }
};

18.2 命名空间

大型程序会有多个独立开发的库,不同库中若有相同的全局名字则会发生命名空间污染,在以前,程序员会把名字起的很长一次来避免和别人重复,但这样会很麻烦(俺觉得有时候起名字比写代码还难).

可以使用命名空间来防止名字冲突,每个命名空间是一个作用域,可以避免全局名字固有的限制.

18.2.1 命名空间定义

使用关键字namespace可以定义命名空间,例如:

namespace my_namespace{
    //将要放在命名空间里的声明和定义放在块内
    //类,变量(包括初始化操作),函数,模板,其他命名空间(!!!)
}    //这里不用分号

命名空间内可以写其他命名空间,但是命名空间不能定义在函数或类的内部.

定义在某个命名空间中的名字可以被本命名空间的其他成员直接访问(不用加作用域),其他地方使用该命名空间的名字则需要加上作用域,例如:

my_namespace:: my_fun();
std::cout<<std::endl;

 命名空间可以是不连续的,上上段代码可以是定义一个新的命名空间,也可以是为已有的命名空间添加新内容.因此可以将命名空间的声明和定义分文件编写.

定义多个类型不相关的命名空间应该使用单独的文件分别表示每个类型.

C++11新标准引入了一种新的嵌套命名空间,称为内联命名空间(inline namespace),内联命名空间可以直接被外层命名空间使用,无需在内联命名空间的名字前添加该命名空间的前缀就可以直接访问.

可以通过不写命名空间名字的方式来定义未命名的命名空间,未命名的命名空间可以不连续但是不能跨文件.

俺没有很理解上面内联命名空间和未命名的命名空间两个玩意存在的必要

18.2.2 使用命名空间成员

每次使用命名空间成员的时候写上前缀会很麻烦,我们可以使用using(之前有介绍过)和起命名空间的别名的方式来简化我们的操作.

下面的例子说明我们可以自己给命名空间起别名并且可以照常使用,而且同一个命名空间可以有多个别名,而且都可以使用.

namespace s = std;
namespace t = std;
namespace d = std;
s::cout<<"hello world"<<s:endl;
t::cout<<"hello world"<<t:endl;
d::cout<<"hello world"<<d:endl;

使用using声明可以引入命名空间的一个成员,有效范围从using声明的地方开始直到using所在的所用于结束,同时外层作用域的同名实体将会被隐藏.

using std::cout;
using std::endl;

 using指示可以使得指出的命名空间的成员可以直接使用.(区分一下using声明和using指示)

using namespace std;

using指示的作用域比using声明的作用域要复杂得多,using指示可以将命名空间成员提升到包含命名空间本身和using指示的最近作用域.using指示使得整个命名空间的内容变得有效.通常情况下命名空间会含有一些不能出现在局部作用域的定义,因此using指示一般被看作是出现在最近的外层作用域中.

头文件如果在顶层作用域中含有using指示或using声明,则会将名字注入到所有包含此头文件的文件中.所以应当避免using指示,而使用using声明.(在命名空间本身的实现文件中可以使用using指示)

如果我们提供了一个对std等命名空间的using指示而未做出特殊控制的话,将重新引入由于使用了多个库而造成的名字冲突问题.

18.2.3 类,命名空间与作用域

除了类内部出现的成员函数定义之外,一般都是向上查找作用域.可以从函数的限定名推断出查找名字时检查作用域的次序,限定名以相反次序指出被查找的作用域.

标准库中的move和forward很容易被名字冲突,因此建议写全称(std::move,std::forward)

18.2.4 重载与命名空间

using声明的是一个名字而不是一个特定的函数,因此using声明的函数名字的形参若是与同名函数不冲突,则会被列入重载函数中

18.3 多重继承与虚继承

多重继承是指从多个直接基类中产生派生类的能力,多重继承的派生类继承所有父类的属性,因此多个基类相互交织产生的细节可能会带来各种问题.

18.3.1 多重继承

派生类的派生列表可以包含多个基类,例如:

class Son: public: Father , public Mother{ };

在多重继承关系中派生类的对象包含有每个基类的子对象.

多重继承的派生类的构造函数初始值只能初始化它的直接基类,派生类的构造函数初始值列表将实参分别传递给每个直接基类,构造顺序与派生类列表中基类的出现顺序保持一致(以上面的为例则是先构造Father类再构造Mother类).而析构函数的调用顺序则与构造函数相反.

继承构造函数的方法如下:

class Son: public Father, public Mother{
    using Father::Father;    //继承Father的构造函数
    using Mother::Mother;    //继承Mother的构造函数
    Son(int a):Father(a),Mother(a){ }    //调用继承来的构造函数
}

C++11新标准中允许派生类从它的一个或多个基类中继承构造函数,但是如果从多个基类中继承了相同的构造函数则会产生错误,此时就必须为该构造函数定义自己的版本.

18.3.2 类型转换与多个基类

在只有一个基类的情况下,派生类的指针或引用能自动转换成一个可访问基类的指针或引用(多态).多个基类则可以将父类的指针指向一个派生类对象.

18.3.3 多重继承下的类作用域

多重继承的情况下,可能会查找到多个基类都有的名字,则会产生二义性(例如Father的一个成员为age,而Mother也有一个成员叫age,则Son使用age时就会产生错误).不过使用时加上作用域则在一般情况下不会有什么太大的问题(例如Father::age,Mother::age),要彻底避免这种二义性最好那就是在派生类中定义一个该名字的新版本.

18.3.4 虚继承

派生类可以多次继承到同一个类,例如下面这种情况

 如果同上图这样继承的话,潜水艇会继承到两份皮划艇的成员,而我们可以通过虚继承的方式将皮划艇变成虚基类,来解决问题.

在派生列表中添加关键字virtual就可以指定虚基类了,例如:

class Son:virtual public Father{ }//此处Son类虚继承了Father类

同上例,在Son后续的派生类中,共享虚基类的同一份实例.

每个共享的虚基类只有一个唯一的共享的子对象,所以该基类的成员可以直接访问而不会产生二义性,但如果虚基类的成员被一条派生路径覆盖,则我们仍然可以直接访问被覆盖的成员,但最好为被覆盖的成员自定义一个新版本.

18.3.5 构造函数与虚继承

在虚派生中,虚基类由最底层的派生类初始化,接下来再按照派生列表中的顺序依次初始化.因此虚基类总是先于非虚基类构造.析构顺序和构造顺序相反.

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/746120.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

(工具记录)Log4j2_RCE

0x00 简介 ApacheLog4j2是一个开源的Java日志框架&#xff0c;被广泛地应用在中间件、开发框架与Web应用中。 0x01 漏洞概述 该漏洞是由于Apache Log4j2某些功能存在递归解析功能&#xff0c;未经身份验证的攻击者通过发送特定恶意数据包&#xff0c;可在目标服务器上执行任意…

获取DNF人物坐标值

众所周知DNF是一个没有坐标值显示的游戏。 那么如何才能不碰内存和封包的情况下&#xff0c;获取DNF游戏中人物在当前房间的坐标值呢&#xff1f; 有兴趣的找我交流吧。

Go语言IO模式

Go语言IO模式 IO 操作是我们在编程中不可避免会遇到的&#xff0c;Go语言的 io 包中提供了相关的接口&#xff0c;定义了相应的规范&#xff0c;不同的数 据类型可以根据规范去实现相应的方法&#xff0c;提供更加丰富的功能。 本文主要介绍常见的 IO (输入和输出)模式&…

《MySQL》复合查询和连接

文章目录 查询单行子查询多行子查询合并查询 连接内连接外连接 点睛之笔&#xff1a;无论是多表还是单表&#xff0c;我们都可以认为只有一张表。 只要是表&#xff0c;就可以查询和连接成新表&#xff0c;所以select出来的结果都可以认为成一张表&#xff0c;既然是一张表&…

Python多线程使用详解

概要 多线程是一种并发编程的技术&#xff0c;通过同时执行多个线程来提高程序的性能和效率。在Python中&#xff0c;我们可以使用内置的threading模块来实现多线程编程。 一、创建线程 在使用多线程之前&#xff0c;我们首先需要了解如何创建线程。Python提供了threading模块…

你信不信,只要学几天javascript就可以使用纯原生实现五星评分效果 【附完整代码】

&#x1f680; 个人主页 极客小俊 ✍&#x1f3fb; 作者简介&#xff1a;web开发者、设计师、技术分享博主 &#x1f40b; 希望大家多多支持一下, 我们一起进步&#xff01;&#x1f604; &#x1f3c5; 如果文章对你有帮助的话&#xff0c;欢迎评论 &#x1f4ac;点赞&#x1…

动态规划DP(七) 股票交易

1.股票交易 在股票买卖的最佳时机问题中&#xff0c;给定一个数组&#xff0c;数组中的每个元素代表某一天的股票价格。你可以进行多次买入和卖出&#xff0c;但是必须在再次购买前卖出之前的股票。目标是找到最大的利润。 动态规划可以用于解决股票交易类的问题&#xff0c;…

ssl证书和域名过期提醒平台

由于经常忘记了证书是否要过期&#xff0c;导致过期了出现一些访问上的问题 docker安装部署 当然可以接入mysql&#xff0c;默认使用的sqlite version: "3" services:domain-admin:image: mouday/domain-admin:latestcontainer_name: domain-adminvolumes:- ./data:/…

菜鸡shader:L8 UV扰动动画——火焰和简单水面

文章目录 卡通火焰代码最后效果 水面代码最后效果 这此做笔记的两个shader其实是课程的作业&#xff0c;课程主要也是讲UV扰动的概念&#xff0c;因为课程的shader在另一台电脑上&#xff0c;做笔记就暂时不带他们了&#xff0c;简单做下火焰和水面的shader。 卡通火焰 火焰这…

FreeRTOS(队列)

队列 什么是队列&#xff1f; 队列又称消息队列&#xff0c;是一种常用于任务间通信的数据结构&#xff0c;队列可以在任务与任务间、中断和任 务间传递信息。 为什么不使用全局变量&#xff1f; 如果使用全局变量&#xff0c;兔子&#xff08;任务1&#xff09;修改了变量 a …

新版studio没法筛选Log

目录 方式一 简单粗暴&#xff0c;针对怀旧者&#xff0c;可切回原版log视图 方式二 学习新的log过滤方法 升级新版本AndroidStudio后&#xff0c;log日志变成以下样子&#xff0c;发现之前过滤error,infor的tab都不见了&#xff0c;瞬间不淡定了&#xff0c;查阅资料才发现…

DAY45:动态规划(五)背包问题:01背包理论基础+二维DP解决01背包问题

文章目录 背包问题大纲01背包01背包暴力解法01背包二维DP解法二维DP数组的解法DP数组含义递推公式初始化二维DP数组&#xff08;比较重要&#xff09;遍历顺序&#xff08;比较重要&#xff09; 二维DP数组完整版思路总结返回值为什么是二维数组最后一个元素DP推导过程与数组含…

selenium 根据期刊信息获取知网文献信息 pt.1

哈喽大家好&#xff0c;我是咸鱼 之前写过一篇获取知网文献信息的文章&#xff0c;看了下后台数据还挺不错 所以咸鱼决定再写一篇知网文献信息爬取的文章 需要注意的是文章只是针对某一特定期刊的爬取&#xff0c;希望小伙伴们把关注点放在如何分析网页以及如何定位元素上面…

python实现前后端学生管理系统(前后端不分离)

⭐作者介绍&#xff1a;大二本科网络工程专业在读&#xff0c;持续学习Java&#xff0c;努力输出优质文章 ⭐作者主页&#xff1a;逐梦苍穹 ⭐所属专栏&#xff1a;项目。 目录 1、前言2、简述实现内容首页注册登录管理员 3、详细代码3.1、项目目录3.2、templates3.2.1、testxz…

赛效:如何在线转换图片格式

1&#xff1a;点击左侧菜单栏里的“格式转换”&#xff0c;然后在转换格式菜单栏里点击上传按钮。 2&#xff1a;选择下方输出格式&#xff0c;点击右下角“开始转换”。 3&#xff1a;稍等片刻转换成功后&#xff0c;点击图片右下角的“下载”&#xff0c;将转换后的图片保存到…

UE5《Electric Dreams》项目PCG技术解析 之 PCGCustomNodes详解(四)ApplyHierarchy

继续解析《Electric Dreams》项目中的自定义节点和子图&#xff1a;ApplyHierarchy 文章目录 前导文章标准组合拳ApplyHierarchyExecute with ContextIteration Loop BodyPoint Loop Body应用场景 小结 前导文章 《UE5《Electric Dreams》项目PCG技术解析 之 理解Assembly&…

【Android】APT与JavaPoet学习与实战

PS&#xff1a;本文讲解的APT全称为Annotation Processing Tool&#xff0c;而非是Android Performance Tuner&#xff0c;这两种工具简称皆为APT&#xff0c;前者是“注释处理工具”&#xff0c;后者是“Android性能调试器”。 本文分别使用Java、kotlin 语言进行开发&#xf…

做一个游戏小项目有多简单?

认识一个朋友&#xff0c;学了很多年的 python, 还停留在 helloworld 阶段&#xff0c;每次拿起又放下&#xff0c;是不是很熟悉&#xff1f;每天都在想&#xff0c;我要学编程&#xff0c;我要学编程&#xff0c;但是又不知道从何学起&#xff0c;学了一点又不知道怎么用&…

java并发编程原理-----线程

目录 上下文切换 java代码创建线程的两种方式 线程的五个状态 线程join方法 多线程之间的影响 上下文切换 CPU的每一个核心同一时刻只能执行一个线程&#xff0c;但是我们会发现电脑同一时刻现实会进行几千个线程&#xff0c;这就是cpu在快速的切换执行线程&#xff0c;由…

Python中的迭代器

一、介绍 在Python中&#xff0c;迭代器是一种访问集合元素的方式&#xff0c;可以用于遍历数据集中的元素&#xff0c;而不需要事先知道集合的大小。迭代器可以被用于循环语句中&#xff0c;例如for循环&#xff0c;来遍历集合中的每个元素。 Python中的迭代器是一个实现了迭…