C++ 入門学習ノート ----- 第 18 章: 大規模プログラム用のツール

1. 例外処理

try
{
    
    
	try
	{
    
    
	}
	catch(bad_alloc)	//捕获特定异常
	{
    
    
		/**处理异常**/			
		throw;			//或者抛出,让外层的异常捕获处理
	}
}
catch(...)	//捕获所有内存未处理的异常
{
    
    
	/**处理异常**/
}

1.3. 関数の try ステートメント ブロックとコンストラクター

构造函数在进入其函数体之前首先执行初始值列表。如果在函数体内部捕获异常,因为在初始值列表抛出异常时构造函数体内的try
语句块还未生效,所以构造函数体内的catch语句无法处理构造函数初始值列表抛出的异常。

函数try语句块使得一组catch语句既能处理构造函数体(或析构函数体),也能处理构造函数的初始化过程(或析构函数的析构过程)。
例如:
template<typename T>
Test<T>::Test(std::initializer_list<T> il) try:vec(il)	
{
    
    	/**空函数体**/  }
catch(const std::bad_alloc &e)
{
    
    	/**处理异常**/	}

还有一种情况值得注意,在初始化构造函数的参数时也可能发生异常,这样的异常不属于函数try语句块的一部分。函数try语句块
只能处理构造函数开始执行后发生的异常。和其他函数调用一样,如果在参数初始化的过程中发生了异常,则该异常属于调用表达式
的一部分,并将在调用者所在的上下文中处理。

1.4.noexcept例外の説明

对于用户即编译器来说,预先指定某个函数不会抛出异常显然大有裨益。
首先,知道函数不会抛出异常有助于简化调用该函数的代码;
其次,如果编译器确认函数不会抛出异常,它就能执行某些特殊的优化操作。

void fun()const & noexcept override {
    
    }typedef或类型别名中则不能出现noexcept.
在成员函数中,noexcept说明符需要跟在const及引用限定符后,而在finaloverride或虚函数=0之前。
所以的声明和定义都要有noexcept,或者都没有。

noexcept可以用在两种情况下:
1.确认函数不会抛出异常。
2.根本不知道如何处理异常。
编译器并不会在编译时检查noexcept说明。

下面两个函数等价:都不会抛出异常
void fun() noexcept;	
void fun() throw();		//旧版本

例外仕様の実パラメータ

noexcept说明符接受一个可选的实参,该实参必须能转换为bool类型。
void fun() noexcept(true);		//不抛出异常

noexcept 演算子

noexcept运算符是一个一元运算符,返回值是一个bool类型的右值常量表达式,表示给定的表达式是否会抛出异常。
和sizeof类似,noexcept也不会求运算对象的值。
noexcept(e)
当e调用的所有函数都做了不抛出说明且e本身不含有throw语句时,上述表达式为true,否则返回falsevoid f() noexcept(noexcept(g()));	//外层的noexcept是异常说明符,内层的是操作符。
f和g的异常说明一致:要异常都异常,不异常都不异常。

例外指定とポインター、仮想関数、およびコピー制御

尽管noexcept说明符不属于函数类型的一部分,但是函数的异常说明仍然会影响函数的使用。
函数指针及该指针所指的函数必须具有一致的异常说明。也就是说,如果我们为某个指针做了不抛出异常的说明,则该指针将只能
指向不抛出异常的函数。如果显示或隐式说明了指针可能抛出异常,则该指针可以指向任何函数。

如果一个虚函数承诺它不会抛出异常,则后续派生出来的虚函数也必须做出同样的承诺。
如果基类的虚函数允许抛出异常,则派生类的对应函数既可以抛出异常,也可以不抛出异常。

当编译器合成拷贝控制成员时,同时也生成一个异常说明。如果对所有成员和基类的所有操作都承诺不会抛出异常,则合成的成员
是noexcept的。如果合成成员调用的任意一个函数可能抛出异常,则合成的成员是noexcept(false).而且,如果我们定义了一个
析构函数但是没有为它提供异常说明,则编译器将合成一个。合成的异常说明将与假设由编译器为类合成析构函数时所得的异常
说明一致。

1.5. 例外クラス階層は、
ここに画像の説明を挿入
独自の例外クラスを定義できます

class MyExcept:public std::runtime_error
{
    
    
public:
	explicit MyExcept(const std::string& s):std::runtime_error(s){
    
    }
}

class MyExcept2:public std::logic_error
{
    
    
public:
	explicit MyExcept(const std::string& s,const std::string& s2):std::runtime_error(s),str(s2){
    
    }
	const std::string str;
}

2. 名前空間

#include <string>
namespace ns
{
    
    
	namespace ns2{
    
    }
	inline namespace ns1{
    
    }	//内联命名空间,外层作用域可直接访问内部的成员	
	namespace{
    
    }				//未命名命名空间,可直接访问成员,每个文件的未命名命名空间不同
}

namespace myns = ns::ns2;	//简化命名空间命名

using-declarations: 簡単な概要

一条using声明语句一次只引入命名空间的一个成员。
using ns::meb;

using 命令: 名前空間のメンバーを外側のスコープに挿入します

using namesapce ns;	//ns命名空间所有的名字都可见,这样就无须添加任何前缀限定符了

3. 多重継承と仮想継承

class Base{
    
    }
class Derived1:public virtual Base{
    
    }		//虚继承
class Derived2:public virtual Base{
    
    }		//虚继承
class SubDerived:public Derived1,public Derived2	//SubDerived中只有一份Base成员
{
    
    
public:
	SubDerived():Base(),Derived1(),Derived2(){
    
    }	//直接调用虚基类的构造函数,进行初始化
}	

初始化:先初始化虚基类的成员(调用虚基类的构造函数),在顺序初始化其他直接基类。
析构:最后执行虚基类的析构函数。

おすすめ

転載: blog.csdn.net/weixin_41155760/article/details/126142354