系列文章目录
文章目录
前言
异常处理、命名空间、多重继承
异常处理
抛出异常
throw
在try内发生了异常,则会一直寻找与该异常匹配的catch
捕获异常
catch子句 exceptrion declaration异常声名
捕获所有异常
void manip()
{
try{
//这里抛出一个异常
}catch (...){
//处理异常
throw;
}
}
函数try语句块与构造函数
template<typename T> Blob<T>::Blob(std::initializer_list<T> il) try:
data(std::make_shared<std::vector<T>>(il)) {/*empty func body*/}
catch (const std::bad_alloc &e){
handle_out_of_memory(e);
}
noexcept异常说明
void recoup(int) noexcept(true/false); //true不会抛出异常,false可能抛出异常
recoup(int) noexcept -> int
mem()const noexcept /final/override = 0
异常说明的实参
noexcept运算符
noexcept(recoup(i)); //如果recoup不抛出异常则结果为true;否则结果为false
void f() noexcept( noexcept( g() ) ); //f和g的异常说明一致
noexcept有两层含义:当跟在函数参数列表后时他是异常说明符;当作为noexcept异常说明的bool实参出现时,它是一个运算符
异常说明与指针、虚函数和拷贝控制
可能会抛出异常的可以指向不会抛出异常的,反之不行。
void (*pf1)(int) noexcept = recoup;
异常类层次
![](https://i-blog.csdnimg.cn/blog_migrate/3267ce38c9019ac314461732cfd16a7d.png)
//为某个书店应用程序设定的异常类
class out_of_stock: public std::runtime_error
{
public:
explicit out_of_stock(const std::string &s):std::runtime_error(s)
{ }
};
class isbn_mismatch: public std::logic_error
{
public:
explicit isbn_mismatch(const std::string &s):std::logic_error(s)
{ }
isbn_mismatch(const std::string &s, const std::string &lhs, const std::string &rhs):
std::logic_error(s), left(lhs), right(rhs) { }
const std::string left, right;
}
命名空间
namespace pollution
命名空间定义
namespace cplusplus{
class Slaes_data {/* xxx */};
class Query {};
class Query_base {};
} //命名空间结束后无须分号,这一点与块类似
//命名空间的名字必须在定义它的作用域内保持唯一
//可以定义在全局作用域内或其他命名空间中,不能定义在类或函数内
每个命名空间都是一个作用域
命名空间可以是不连续的
namespace nsp{
// 相关声名
}
//我分开了
namespace nsp{
//xxx
}
//可以在命名空间之外定义成员
- 命名空间的一部分成员的作用时定义类,以及声名作为类接口的函数及对象,则这些成员应该置于头文件中,这些头文件将被包含在使用了这些成员的文件中
- 命名空间成员的定义部分置于另外的源文件中
定义命名空间成员
模板特例化
全局命名空间
在所有类、函数及命名空间之外的空间,以隐式的方式声名。全局作用域中定义的名字被隐式的添加到全局命名空间中。
::member_name
表示全局命名空间中的一个成员
命名空间支持嵌套
内联命名空间
//关键字inline必须出现在命名空间第一次定义的地方
namespace all_namespace
{
inline namespace FifthEd
{
int iname = 12;
}
namespace FifthEdyyy
{
int iname = 12;
}
//若两个都是内联函数且名称相同,
}
int main()
{
all_namespace::FifthEdyyy::iname;
all_namespace::iname ;
//则无法all_namespace::iname ;
}
未命名的命名空间
可以在给定的文件内不连续,不能跨越多个文件
静态声名周期
int i; // i的全局声名
namespace
{
int i;
}
//二义性
i = 10;//❌报错
namespace local
{
namespace
{
int i;
}
}
local::i = 43; //正确
使用命名空间成员
命名空间的别名
namespace P = cplus_cplus;
namespace P = cplus_cplus::second ;
using声名:扼要概述
从using开始到 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; //✔
void manip()
{
//using 指示,blip中的名字被“添加”到全局作用域中
using namespace blip; // 如果使用了j,则将在::j和blip::j之间产生冲突
++i; //将blip::i = 17
++j; //❌二义性错误
++::j; //✔
++blip::j; //✔
int k = 97; //当前局部k隐藏了blip::k
++k; //将当前局部的k设定为98
}
类、命名空间与作用域
实参相关的找查与类类型形参
找查与std::move和std::forward
友元声名与实参相关的找查
namespace A
{
class C
{
// 两个友元,在友元声名之外没有其他的声名
// 这些函数隐式的成为命名空间A的成员
friend void f2(); // 除非另有声名,否则不会被找到
friend void f(const c&); // 根据实参相关的找查规则可以被找到
}
}
int main()
{
A::C cobj;
f(cobj); //正确:
f2(); //❌
}
重载与命名空间
与实参相关的找查与重载
![](https://i-blog.csdnimg.cn/blog_migrate/a4aa1ff3113bc8ff7f4b75dc7c41ee47.png)
重载与using声名
重载与using指示
跨多个using指示的重载
如果存在多个using指示,则来自每个命名空间的名字都会成为候选函数集的一部分
多重继承与虚函数
多重继承
多重继承的派生类从每个基类中继承状态
派生类构造函数初始化所有基类
继承的构造函数与多重继承
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定义了它自己的构造函数,则必须出现
};
析构函数与多重继承
多重继承的派生类的拷贝与移动操作
类型转换与多个基类
多重继承下的类作用域
当一个类有多个基类时,有可能出现派生类从两个或更多基类中继承了同名成员的情况。此时,不加前缀限定符直接使用改名字将引发二义性
虚继承
虚继承,虚基类
![](https://i-blog.csdnimg.cn/blog_migrate/534c1e28a8e47aeb39e42cc8d2ed0817.png)
使用虚基类
class Raccoon : public virtual ZooAnimal { /* */ };
class Bear : virtual public ZooAnimal { /* */ };
class Panda : public Bear, public Raccoon, public Endangered
{
//
};
virtual说明符表明了一种愿望,及在后续的派生类当中共享虚基类的同一份实例。
支持向基类的常规类型转换
虚基类成员的可见性
构造函数与虚继承
虚继承的对象的构造方式
首先使用提供给最底层派生类构造函数的初始值初始化该对象的虚基类子部分,接下来按照直接基类在派生列表中出现的次序依次对其进行初始化。
如:
- 首先使用panda的构造函数初始值列表中提供的初始值构造虚基类ZooAnimal部分
- 构造bear部分
- Raccoon部分
- 然后构造第三个直接基类Endangered
- 最后构造Panda部分
如果没有显示的初始化ZooAnimal基类,则ZooAnimal的默认构造函数将被调用
构造函数与析构函数的次序
编译器按照直接基类的声名顺序对其依次进行检查,以确定其中是否含有虚基类。如果有,则先构造虚基类,然后按照声名的顺序逐一构造其他非虚基类。
对象的销毁顺序与构造顺序正好相反
总结
构造函数顺序:在非虚继承中,基类的构造顺序与其在派生列表中出现的顺序一致。在虚继承中,首先构造虚基类。虚基类的构造顺序与其在派生类的派生列表中出现的顺序一致。只有最底层的派生类才能初始化虚基类。虚基类的初始值如果出现在中间基类中,则这些初始值将被忽略。