本文应该是在年前写完的,结果由于要回家参加同学婚礼,走的太过匆忙,所以年后回到学校才写完。在写本篇博客时,我尝试使用了 PC版的讯飞输入法,直接可以将大段的文字通过语音的方式码进来,写作效率唰唰的提高。另外,有些书中的代码也比较长,敲起来也比较费时费力。这是使用在线文字识别来导入的,可以转换成带有格式的 Word文档,从而在复制粘贴到 MarkDown中,之后再调整下格式后就 OK了。我还是觉得作业部落的排版更好看一点,所以这里也把本文在作业部落的链接也放在这里了。外貌协会的同学可以点击这里。
标签: C++Primer 学习记录 异常处理 命名空间 多重继承
try {
// 使用 C++标准库
} catch (const runtime_error &re) {
// ...
} catch (exception) { // 忽略形参名字
// ...
}
catch
语句会改变其参数的内容。如果在改变了参数的内容后 catch
语句重新抛出异常,则只有当 catch
语句是引用类型时,我们对参数所做的改变才会被保留并继续传播。
catch (my_err &eObj) { // 引用类型
eObj.status = errCodes::servereErr; // 修改了异常对象
throw; // 异常对象的 status成员是 servereErr
} catch (other_error eObj) { // 非引用类型
eObj.status = errCodes::servereErr; // 只修改了异常对象的局部副本
throw; // 异常对象的 status成员没有改变
}
catch(...)
语句,可以捕获所有异常,与任意类型的异常匹配。如果 catch(...)
语句与其他几个 catch语句一起出现,则该语句必须在最后的位置。出现在捕获所有异常语句后面的 catch语句将永远不会被匹配。
函数 try语句块
的形式,使得对应的 catch语句技能处理构造函数体,也能处理构造函数的初始化过程。其形式如下:
template <typename T>
Blob<T>::Blob() try :
data(std::make_shared<std::vector<T>>(i1)) {
/* 空函数体 */
} catch(const std::bad_alloc &e) { handle_out_of_memory(e); }
noexcept说明符
可以指定某个函数不会抛出异常。
// 尽管函数明显违反了异常说明,但它仍然可以顺利编译通过
void f() noexcept // 承诺不会抛出异常
{
throw exception(); // 违反了异常说明
}
void recoup(int) noexcept(true); // recoup不会抛出异常
void alloc(int) noexcept(false); // alloc可能抛出异常
noexcept(recoup(i)) // 如果 recoup不抛出异常,则结果为 true;否则为 false
void f() noexcept( noexcept(g(i)) ); // f和 g的异常说明一致
// recoup和 pf1都承诺不会抛出异常
void (*pf1)(int) noexcept = recoup;
// 正确,recoup不会抛出异常,pf2可能抛出异常,二者之间互不干扰
void (*pf2)(int) = recoup;
pf1 = alloc; // 错误,alloc可能抛出异常,但是 pf1已经说明了它不会抛出异常
pf2 = alloc; // 正确,pf2和 alloc都可能抛出异常
class Base {
public:
virtual double fl(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做了更严格的限定
};
#include
放在命名空间内部。
// --- Sales_data.h --- // #include应该出现在打开命名空间的操作之前 #include <string> namespace cplusplus_primer { class Sales_data { /* ... */}; Sales_data operator+(const Sales_data&, const Sales_data&); // Sales_data的其他接口函数的声明 } // --- Sales_data.cc --- // 确保 #include出现在打开命名空间的操作之前 #include "Sales_data.h" namespace cplusplus_primer { // Sales_data成员及重载运算符的定义 } // --- user.cc --- // Sales_data.h头文件的名字位于命名空间 cplusplus_primer中 #include "Sales_data.h" int main() using cplusplus_primer::Sales_data; Sales_data transl, trans2; // ... return 0;
::member_name
这种形式,可以表示全局命名空间中的一个成员。
out_nsp::in_nsp::member_name
。
namespace
前添加关键字 inline
,关键字 inline
必须出现在命名空间第一次定义的地方,后续再打开命名空间的时候可以写 inline
,也可以不写。
inline namespace FifthEd {
// 该命名空间表示本书第 5 版的代码
}
namespace FifthEd { // 隐式内联
/* ... */
}
cplusplus_primer
将同时使用这两个命名空间,并且假定每个命名空间都定义在同名的头文件中,则命名空间 cplusplus_primer
, 可以定义成如下形式。形如 cplusplus_primer::
的代码可以直接获得 FifthEd
的成员,想要使用较早期版本的代码则只需加上完整的内层命名空间的名字,如 cplusplus_primer::FourthEd::Item_base
。namespace FourthEd {
class Item_base { /* ... */ };
// 本书第 4 版用到的其他代码
}
namespace cplusplus_primer {
#include "FifthEd.h"
#include "FourthEd.h"
}
namespace
后紧跟花括号括起来的一系列声明语句。未命名的命名空间中定义的变量拥有静态生命周期:他们在第一次使用前创建,并且直到程序结束时才销毁。int i; // i的全局声明
namespace {
int i;
}
// 二义性: i的定义既出现在全局作用域中,又出现在未嵌套的未命名的命名空间当中
i = 10;
```C
namespace local { namespace { int i; } } // 正确:定义在嵌套的未命名的命名空间中的 i与全局作用域中的 i不同 local::i = 10;
14. 命名空间的别名,使得我们可以为命空间的名字设定另一个短得多的同义词。如 `namespace cp = cplusplus_primer;`
15. **using声明**一次只引入命名空间的一个成员,有效范围从声明的地方开始,一直到声明所在的作用域结束为止。在此过程中外层作用域的同名实体将被隐藏。using声明可以出现在全局作用域、局部作用域、命名空间作用域以及类的作用域中。在类的作用域中,这样的声明语句只能指向基类成员。
16. **using指示**一次性注入某个命名空间的所有名字,using指示可以出现在全局作用域、局部作用域和命名空间作用域中,但是不能出现在类的作用域中。**using指示所注入的名字一般被看作是出现在最近的外层作用域中。**
17. 对于如下代码,分别在“位置1”和“位置2”的地方,使用 using声明或 using指示时, manip中的 3个名字实际所指示的对象或所在作用域如下表所示。
```C
namespace Exercise {
int ivar = 0;
double dvar = 0;
const int limit = 1000;
}
int ivar = 0;
// 位置1
void manip() {
//位置2
double dvar = 3.1416;
int iobj = limit + 1;
++ivar;
using声明和 using指示的作用域不同
std::string s;
operator>>(std::cin, s);
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没有被声明
}
namespace NS {
class Quote { /* ... */ };
void display(const Quote&) { /* ... */ };
}
// Bulk_item的基类声明在命名空间NS中
// 与实参相关的查找,即使没有使用 using说明,也将相关函数变为可见
class Bulk_item : public NS::Quote { /* ... */ };
int main() {
Bulk_item bookl;
display(bookl);
return 0;
}
using NS::print(int); // 错误,不能指定形参列表
using NS::print; // 正确,using声明只声明一个名字