第十八章 用于大型程序的工具

news2024/11/7 5:38:11

18.1 异常处理

  1. 异常处理机制允许程序中独立开发的部分能够在运行时就出现的问题进行通信并做出相应的处理。异常使得问题的检测与解决过程分离开来。

18.1.1 抛出异常

  1. 当执行一个throw时,跟在throw后面的语句将不再被执行。相反,程序的控制权从throw转移到与之匹配的catch模块。
  2. 当程序抛出一个异常时,程序暂停当前函数的执行过程并立即开始查找最邻近的与异常匹配的 catch 子句。
  3. 一个异常如果没有被捕获,程序将调用标准库函数 terminate,它将终止当前的程序。
  4. 异常传递过程中,当退出了某些作用域时,该作用域内异常发生前创建的局部对象会被销毁。
  5. 栈展开过程中对象被自动销毁。
  6. 栈展开过程沿着嵌套函数的调用链不断查找,直到找到匹配的catch子语句。
  7. 在栈展开的过程中,运行类类型的局部对象的析构函数。因为这些析构函数是自动执行的,所以它们不应该抛出异常。一旦在栈展开的过程中析构函数抛出了异常,并且析构函数自身没能捕获到该异常,则程序将被终止。
  8. 当抛出一条表达式时,该表达式的静态编译时类型决定了异常对象的类型。
  9. 抛出指针要求在任何对应的处理代码存在的地方,指针所指的对象都必须存在。

18.1.2 捕获异常

  1. 通常情况下,如果catch接受的异常与某个继承体系有关,则最好将该catch的参数定义成引用类型。
  2. 如果在多个catch语句的类型之间存在着继承关系,则应该把继承链最底端的类放在前面,而将继承链最顶端的类放在后面。
  3. 一条catch语句通过重新抛出的操作将异常传递给另外一个catch语句。
  4. 如果catch(…)与其他几个catch语句一起出现,则catch(…)必须在最后的位置。出现在捕获所有异常语句后面的catch语句将永远不会被匹配。

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

  1. 处理构造函数初始值异常的唯一方法是将构造函数写成函数try语句块(函数测试块)。

18.1.4 noexcept异常说明

  1. 通过提供noexcept说明指定某个函数不会抛出异常。(C++11
  2. noexcept可以用在两种情况下:一是确认函数不会抛出异常,二是根本不知道该如何处理异常。
  3. 通常情况下,编译器不能也不必在编译时验证异常说明。
  4. noexcept有两层含义:当跟在函数参数列表后面时它是异常说明符;而当作为noexcept异常说明的bool实参出现时,它是一个运算符。
  5. 函数指针声明为noexcept,那么指向的函数必须一致。
void alloc(int) noexcept(false); //alloc可能抛出异常
void recoup(int) noexcept(true); //recoup不会抛出异常
//recoup和pf1都承诺不会抛出异常
void (*pf1)(int) noexcept = recoup;
//正确:recoup不会抛出异常,pf2可能抛出异常,二者之间互不干扰
void (*pf2)(int) = recoup;

pf1 = alloc; //错误:alloc可能抛出异常,但是pf1已经说明了它不会抛出异常
pf2 = alloc; //正确:pf2和alloc都可能抛出异常
  1. 如果虚函数承诺不会抛出异常,则派生出来的虚函数也必须做出相同的承诺。
class Base {
public:
	virtual double f1(double) noexcept; 	//不会抛出异常
	virtual int f2() noexcept(false); 		//可能会抛出异常
	virtual void f3(); 			//可能抛出异常
};
class Derived: public Base {
public:
	double f1(double); 	   //错误:Base::f1承诺不会抛出异常
	int f2() noexcept(false); //正确:与Base::f2的异常说明一致
	void f3() noexcept; 	   //正确:Derived的f3做了更严格的限定,是允许的
};

18.1.5 异常类层次

  1. 和其他继承体系一样,异常类也可以看作按照层次关系组织的。层次越低,表示的异常情况就越特殊。
    在这里插入图片描述

18.2 命名空间

  1. 多个库将名字放置在全局命名空间中将引发命名空间污染。
  2. 命名空间分割了全局命名空间,其中每个命名空间是一个作用域。
namespace cplusplus_primer {
	class Sales_data { /*...*/};
	Sales_data operator+(const Sales_data&, const Sales_data&);
	class Query{/*...*/};
} //命名空间结束后无须分号

cplusplus_primer::Query q = cplusplus_primer::Query("hello");
//假设还有另一个命名空间AddisonWesley也提供了一个Query类
AddisonWesley::Query q = AddisonWesley::Query("hello");

18.2.1 命名空间定义

  1. 只要能出现在全局作用域中的声明就能置于命名空间内,主要包括:类、变量(及其初始化操作)、函数(及其定义)、模板和其他命名空间。
  2. 命名空间不能定义在函数或类的内部。
  3. 命名空间作用域后面无须分号。
  4. 命名空间可以是不连续的。
//----Sales_data.h----
//#include应该出现在打开命名空间的操作之前
#include <string>
namespace cplusplus_primer {
	class Sales_data {/*...*/};
	Sales_data operator+{const Sales_data&, const Sales_data&);
	//...
}
//----Sales_data.cc----
//确保#include出现在打开命名空间的操作之前
#include "Sales_data.h"
namespace cplusplus_primer{
	//Sales_data成员及重载运算符的定义
}
//---user.cc---
#include "Sales_data.h" {
	using cplusplus_primer::Sales_data;
	Sales_data trans1, trns2;
	//...
	return 0;
}
  1. 定义多个类型不相关的命名空间应该使用单独的文件分别表示每个类型(或关联类型构成的集合)。
  2. 通常情况下,不把#include放在命名空间内部。
  3. 模板特例化必须定义在原始模板所属的命名空间中。
  4. 全局命名空间以隐式的方式声明,并且在所有程序中都存在。访问方式为::member_name。
namespace cplusplus_primer {
	//第一个嵌套的命名空间:定义了库的Query部分
	namespace QueryLib {
		class Query {/*...*/};
		Query operator&(const Query&, const Query&);
		//...
	}
	//第二个嵌套的命名空间:定义了库Sales_data部分
	namespace Bookstore {
		class Quote{/*...*/};
		class Disc_quote:public Quote{/*...*/};
		//...
	}
}

cplusplus_primer::QueryLib::Query
  1. 和普通的嵌套命名空间不同,内联命名空间(C++11)中的名字可以被外层命名空间直接使用。
//关键字inline必须出现在命名空间第一次定义的地方
inline namespace FifthEd {
	//该命名空间表示本书第5版的代码
}
namespace FifthEd { //隐式内联
	class Query_base {/*...*/};
	//其他与Query有关的声明
}
namespace FourthEd {
	class Item_base {/*...*/};
	class Query_base {/*...*/};
	//本书第四版用到的其他代码
}

namespace cplusplus_primer {
	#include "FifthEd.h"
	#include "FourthEd.h"
}
  1. 当应用程序的代码在一次发布和另一次发布之间发生了改变时,常常会用到内联命名空间。
  2. 未命名的命名空间中定义的变量拥有静态生命周期:它们在第一次使用前创建,并且直到程序结束才销毁。
int i; //i的全局声明
namespace {
	int i;
}
//定义在未命名空间中的名字可以直接使用
//二义性:i的定义出现在全局作用域、又出现在未命名的作用域
i = 10;

namespace local {
	namespace {
		int i;
	}
}
//正确:与全局作用域中的i不同
local::i = 42; 
  1. 和其他命名空间不同,未命名的命名空间仅在特定的文件内部有效,其作用范围不会横跨多个不同的文件。
  2. 如果一个头文件定义了未命名的命名空间,则该命名空间中定义的名字将在每个包含了该头文件的文件中对应不同实体。
  3. 使用未命名的命名空间取代文件中的静态声明。
  4. 命名空间的别名
namespace cplusplus_primer {/*...*/};
namespace primer = cplusplus_primer;
//指向一个嵌套的命名空间
namespace Qlib = cplusplus_primer::Querylib;
Qlib::Query q;

18.2.2 使用命名空间成员

  1. 不能在命名空间还没有定义前就声明别名,否则将产生错误。
  2. 一个命名空间可以有好几个同义词或别名,所有别名都与命名空间原来的名字等价。
  3. 一条using声明语句一次只引入命名空间的一个成员。
  4. using指示与声明不同,所有名字都可见。
  5. using指示不能出现在类的作用域中。
  6. 如果提供了一个对std等命名空间的using指示而未做任何特殊控制的话,将重新引入由于使用了多个库而造成的名字冲突问题。
  7. 头文件最多只能在它的函数或命名空间内使用using指示或using声明。
  8. using指示也并非一无是处,例如在命名空间本身的实现文件中就可以使用using指示。
//命名空间A和函数f定义在全局作用域中
namespace A {
	int i,j;
}
void f() {
	using namespace A; 	//把A中的名字注入到全局作用域中
	cout << i * j << endl; 	//使用命名空间A中的i和j
	//...
}

namespace blip {
	int i = 16, j = 15, k = 23;
	//其他声明
}
int j = 0; //正确:blip的j隐藏在命名空间中
void manip() {
	//using指示,blip中的名字被“添加”到全局作用域中
	using namesapce blip; //如果使用了j,将在::j和blip::j之间产生冲突
	++i; 		//将blip::i设定为17
	++j; 		//二义性错误
	++::j; 	//正确:将全局的j设定为1
	++blip::j; 	//正确:16
	int k = 97; 	//当前局部的k隐藏了blip::k
	++k; 	//将当前局部的k设定为98
}

18.2.3 类、命名空间与作用域

  1. 命名空间内部名字查找规则:即由内向外依次查找每个外层作用域。
  2. 可以从函数的限定名推断出查找名字时检查作用域的次序,限定名以相反次序指出被查找的作用域。
  3. 对于接受类类型实参的函数来说,其名字查找将在实参类所属的命名空间中进行。
std::string s;
std::cin >>s;
//等价于operator>>(std::cin,s);
//为什么operator>>可以被直接调用
  1. 友元声明与实参相关的查找
namespace A {
	class C {
		//两个友元,在友元声明之外没有其他的声明
		//这些函数隐式地成为命名空间A的成员
		friend void f2(); 		//除非另有声明,否则不会被找到
		friend void f(const C&); 	//根据实参相关的查找规则可以被找到
	};
}
int main() {
	A::C cobj; 
	f(cobj);	 //正确:通过在A::C中的友元声明找到A::f
	f2();		 //错误:A::f2没有被声明
}

18.2.4 重载与命名空间

  1. 一个using声明囊括了重载函数的所有版本以确保不违反命名空间的接口。
  2. 一个using声明引入的函数将重载该声明语句所属作用域中已有的其他同名函数。

18.3 多重继承与虚继承

18.3.1 多重继承

  1. 多重继承关系中,派生类的对象包含有每个基类的子对象。
//派生类的派生列表中可以包含多个基类
class Bear:public zooAnimal { };
class Panda:public Bear, public Endangred {/*...*/};

在这里插入图片描述

  1. 构造一个派生类的对象将同时构造并初始化它的所有基类子对象。
//显式地初始化所有基类
Panda::Panda(std::string name, bool onExhibit)
	:Bear(name, onExhibit, "Panda"), Endangered(Endangered::critical) { }
//隐式地使用Bear的默认构造函数初始化Bear子对象
Panda::Panda() 
	:Endangered(Endangered::critical) { }
  1. 与单继承一样,多重继承的派生类构造函数初始值也只能初始化它的直接基类。
//panda对象按如下次序进行初始化:
ZooAnimal --> Bear --> Endangered --> Panda
//panda对象析构函数的调用顺序:
Panda --> Endangered --> Bear --> ZooAnimal
  1. 基类的构造顺序与派生列表中基类的出现顺序保持一致,而与派生类构造函数初始值列表中基类的顺序无关。
  2. 如果一个类从它的多个基类中继承了相同的构造函数,则这个类必须为该构造函数定义它自己的版本。
struct Base1 {
	Base1() = default;
	Base1(const std::string&);
	Base1(std::shared_ptr<int>);
};
struct Base2 {
	Base2() = default;
	Base2(const std::string);
	Base2(int);
};
//错误:D1试图从两个基类中都继承D1::D1(const string&)
struct D1:public Base1,public Base2 {
	using Base1::Base1; //从Base1继承构造函数
	using Base2::Base2; //从Base2继承构造函数
};
//正确:为构造函数定义它自己的版本
struct D2:public Base1,public Base2 {
	using Base1::Base1; //从Base1继承构造函数
	using Base2::Base2; //从Base2继承构造函数
	//D2必须自定义一个接受string的构造函数
	D2(const string &s):Base1(s),Base2(s) { }
	D2() = default;//D2定义了它自己的构造函数
		  //则必须出现
};

18.3.2 类型转化与多个基类

  1. 多重继承中,可以令某个可访问基类的指针或引用直接指向一个派生类对象。
  2. 与单继承一样,对象、指针和引用的静态类型决定了能够使用哪些成员。
//接受Panda的基类引用的一系列操作
void print(const Bear&);
void highlight(const Endangered&);
ostream& operator<<(ostream&, const zooAnimal&);
Panda ying_yang("ying_yang");
print(ying_yang); 		//把一个panda对象传递给一个Bear的引用
hightlight(ying_yang); 	//把一个panda传递给一个Endangered的引用
cout << ying_yang << endl; 	//把一个panda对象传递给zooAnimal的引用
//对编译器而言,转换到任意一种基类都一样好
void print(const Bear&);
void print(const Endangered&);
Panda ying_yang("ying_yang");
print(ying_yang); //二义性错误

18.3.3 多重继承下的类作用域

  1. 当一个类拥有多个基类时,有可能出现派生类从两个或更多基类中继承了同名成员的情况。此时,不加前缀限定符直接使用该名字将引发二义性。
  2. 要想避免潜在的二义性,最好的办法是在派生类中为该函数定义一个新版本。
//如果zooAnimal和Endangered都定义了名为max_weight的成员
//并且Panda没有定义该成员,则下面的调用是错误的:
double d = ying_yang.max_weight();

//为了避免二义性错误,最好的办法是为派生类提供一个版本
double Panda::max_weight() const
{
	return std::max(zooAnimal::max_weight(),Endangered::max_weight());
}

18.3.4 虚继承

  1. 虚继承的目的是令某个类做出声明,承诺愿意共享它的基类。其中,共享的基类子对象称为虚基类。在这种机制下,不论虚基类在继承体系中出现了多少次,在派生类中都只包含唯一一个共享的虚基类子对象。
    在这里插入图片描述

  2. 必须在虚派生的真实需求出现前就已经完成虚派生的操作。

  3. 虚派生只影响从指定了虚基类的派生类中进一步派生出的类,它不会影响派生类本身。

  4. 如果某个类指定了虚基类,则该类的派生仍按常规方式进行。

  5. 不论基类是不是虚基类,派生类对象都能被可访问基类的指针或引用操作。

//关键字public和virtual的顺序随意
class Raccoon:public virtual zooAnimal {/*...*/};
class Bear:virtual public zooAnimal {/*...*/};

class Panda:public Bear, public Raccoon, public Endangered{}

void dance(const Bear&);
void rummage(const Raccoon&);
osgream& operator<<(ostream&,const zooAnimal&);
Panda ying_yang;
dance(ying_yang);	//正确:把一个Panda对象当成Bear传递
rummage(ying_yang); 	//正确:把一个Panda对象当成Raccoon传递
cout << ying_yang;		//正确:把一个Panda当成zooAnimal传递

18.3.5 构造函数与虚继承

  1. 在虚派生中,虚基类是由最低层的派生类初始化的。
  2. 含有虚基类的对象的构造顺序:首先使用提供给最低层派生类构造函数的初始值初始化该对象的虚基类子部分,接下来按照直接基类在派生列表中出现的次序依次对其进行初始化。
  3. 虚基类总是先于非虚基类构造,与它们在继承体系中的次序和位置无关。
Bear::Bear(std::string name, bool onExhibit):
	zooAnimal(name, onExhibit, "Bear") { }
Raccoon::Raccoon(std::string name, bool onExhibit):
	zooAnimal(name, onExhibit, "Raccoon") { }
Panda::Panda(std::string name, bool onExhibit) :
	ZooAnimal(name, onExhibit, "Panda"),
	Bear(name,onExhibit),
	Raccoon(name,onExhibit),
	Endangered(Endangered::critical),
	sleeping_flag(false) { }

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

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

相关文章

红米K80入网,骁龙8至尊版加持主打极致性价比

小米年度旗舰小米 15、15Pro 已经于本周二正式发布&#xff0c;不过 4499 元与 5299 元起售价想必劝退了不少追求极致性价比的小伙伴儿。 于是有同学表示&#xff1a;接下来人民的希望就全看旗舰捍门员红米了。 好消息是&#xff0c;目前新款红米 K80 系已获入网许可&#xff0…

ARM base instruction -- bfi

Bitfield Insert copies a bitfield of <width> bits from the least significant bits of the source register to bit position <lsb> of the destination register, leaving the other destination bits unchanged. 位域插入将<width>位的位域从源寄存器的…

uicc.hci.service的理解

一、uicc.hci.framework的java类 (1) HCIDevice i : getHCIservice 判断获取的service能否实现&#xff0c;若可以则调用并实现serviceimp&#xff0c;并记录appid。 ii : isHCIServiceAvaliable 用于获取service可用性的信息&#xff0c;返回0代表可用。 二、uicc.hci.servic…

更安全高效的文件传输工具,Ftrans国产FTP替代方案可以了解

文件传输协议&#xff08;FTP&#xff09;&#xff0c;诞生于1971年&#xff0c;自20世纪70年代发明以来&#xff0c;FTP已成为传输大文件的不二之选。内置有操作系统的 FTP 可提供一个相对简便、看似免费的文件交换方法&#xff0c;因此得到广泛使用。 随着企业发展过程中新增…

在元神操作系统启动时自动执行任务脚本

1. 背景 本文主要介绍让元神操作系统启动时自动执行任务脚本的方法&#xff0c;适用于无人化任务执行目的。将任务脚本及相关的应用程序准备好之后&#xff0c;把装有元神操作系统的U盘插入目标电脑&#xff0c;然后打开电脑电源就会自动完成所设置的任务。 2. 方法 &#x…

优雅的LUA数据记录方法-serpent序列化+LUA Table

目录 简述如何集成&#xff1f;如何使用序列化 反序列化 参考 简述 项目里需要使用LUA脚本将数据记录到文件&#xff0c;要方便的增加、查找、删除&#xff0c;要方便的加载与存回。 使用序列化/反序列化 lua table可以很容易实现这些功能。 序列化将table转换为字符串 反序列…

jmeter脚本-请求体设置变量and请求体太长的处理

目录 1、查询接口 1.1 准备组织列表的TXT文件&#xff0c;如下&#xff1a; 1.2 添加 CSV数据文件设置 &#xff0c;如下&#xff1a; 1.3 接口请求体设置变量&#xff0c;如下&#xff1a; 2、创建接口 2.1 见1.1 2.2 见1.2 2.3 准备创建接口的请求体TXT文件&#xff…

Sonatype Nexus 部署手册

文章目录 一、前言二、软件环境2.1 版本变更&#xff1a;2.1.1 变更存储的原因2.2.2 H2作为存储的注意点 三、资源配置四、开始部署4.1 部署jdk174.2 离线部署nexus4.2.1 下载4.2.2 部署1. 上传到服务器2. 解压3. 添加用户4. 修改启动参数5. 迁移sonatype-work &#xff0c;并授…

servlet开发

一、Servelet &#xff08;一&#xff09;Servelet概述 1.概念&#xff1a;Severlet是一门动态web资源开发技术。 2.本质&#xff1a;Severlet本质是一段Java程序&#xff0c;但和Java程序不同的是&#xff0c;Severlet程序无法独立运行&#xff0c;需要放服务器中&#xff…

opencv - py_imgproc - py_houghlines and py_houghcircles 霍夫线和圆变换

文章目录 1.霍夫线变换目标理论 OpenCV 中的霍夫变换概率霍夫变换其他资源 2.霍夫圆变换目标理论 1.霍夫线变换 目标 在本章中&#xff0c; 我们将了解霍夫变换的概念。我们将了解如何使用它来检测图像中的线条。我们将看到以下函数&#xff1a;cv.HoughLines()、cv.HoughLi…

数据结构 ——— 用堆解决TOP-K问题

目录 何为TOP-K问题 用堆解决TOP-K问题 代码实现 何为TOP-K问题 比如&#xff1a;整个专业的前10名&#xff0c;世界500强&#xff0c;富豪榜&#xff0c;游戏中前100的活跃玩家等 对于 TOP-K 问题&#xff0c;能想到的最简单直接的方式就是排序 但是&#xff0c;如果数据…

开发之翼:划时代的原生鸿蒙应用市场开发者服务

前言 随着"纯血鸿蒙" HarmonyOS NEXT在原生鸿蒙之夜的正式发布&#xff0c;鸿蒙生态正以前所未有的速度蓬勃发展。据知已有超过15000个鸿蒙原生应用和元服务上架&#xff0c;覆盖18个行业&#xff0c;通用办公应用覆盖全国3800万多家企业。原生鸿蒙操作系统降低了接…

WAF+AI结合,雷池社区版的强大防守能力

网上攻击无处不不在&#xff0c;为了保护我自己的网站&#xff0c;搜索安装了一个开源免费的WAF 刚安装完成就收到了海外的攻击&#xff0c;看到是海外的自动化攻击工具做的 雷池刚好也有AI分析&#xff0c;于是就尝试使用这个功能&#xff0c;看看这个ai能力到底怎么样 以下…

Elasticsearch —— ES 环境搭建、概念、基本操作、文档操作、SpringBoot继承ES

文章中会用到的文件&#xff0c;如果官网下不了可以在这下 链接: https://pan.baidu.com/s/1SeRdqLo0E0CmaVJdoZs_nQ?pwdxr76 提取码: xr76 一、 ES 环境搭建 注&#xff1a;环境搭建过程中的命令窗口不能关闭&#xff0c;关闭了服务就会关闭&#xff08;除了修改设置后重启的…

CSP2024 游记

又是一年 CSP。。。 10 月 5 日&#xff0c;终于过 S 初赛了。。。 然后开始漫长的备战。。 在考试开始前 1 day&#xff0c;我还在兢兢业业地学习图论。然后发现没有考。。。 10 月 25 日下午 15:30&#xff0c;来到 CQBS 试机。我想&#xff0c;怎么测试性能呢&#xff1…

opencv - py_imgproc - py_grabcut GrabCut 算法提取前景

文章目录 使用 GrabCut 算法进行交互式前景提取目标理论演示 使用 GrabCut 算法进行交互式前景提取 目标 在本章中 我们将了解 GrabCut 算法如何提取图像中的前景我们将为此创建一个交互式应用程序。 理论 GrabCut 算法由英国剑桥微软研究院的 Carsten Rother、Vladimir K…

视频制作软件新手必备:8款剪辑工具剪辑思路分享!

随着视频的高度发展&#xff0c;视频已成为一种重要的工具&#xff0c;用以学习娱乐、记录生活点滴以及传递各类信息。不论是制作个人MV、进行企业宣传&#xff0c;还是创作短视频内容&#xff0c;拥有一款功能恰当的视频剪辑软件都显得至关重要。对于初学者而言&#xff0c;选…

无人机避障——路径规划篇(一) JPS跳点搜索算法A*算法对比

JSP 跳点搜索算法与改进 A*算法对比 一、算法概述: 跳点搜索(Jump Point Search,JPS)算法:一种用于路径规划的启发式搜索算法。它主要用于在网格地图(如游戏地图、机器人运动规划地图等)中快速找到从起点到终点的最短路径。该算法在改进 A*算法的基础上进行了优化,通过跳过一…

解决Linux安装Anaconda后出现的conda: command not found问题

参考链接&#xff1a;解决Linux安装Anaconda后出现的conda: command not found问题-百度开发者中心

AI直播带货场景切换模块的搭建!

AI直播带货&#xff0c;作为电商领域的新宠&#xff0c;正以其独特的魅力和高效的营销手段&#xff0c;引领着销售模式的新变革。 在AI直播带货中&#xff0c;场景切换模块是不可或缺的一部分&#xff0c;它不仅能够提升观众的观看体验&#xff0c;还能更好地展示商品&#xf…