还记得2005年那个夏天,高考结束的我到沈阳北方图书城买了一本C++教材。当时在学校里,班主任曾经说计算机程序之前都是用BASIC编写的,后来有了C语言。当时我对计算机程序设计没有什么概念,仅有的经验是自己在文曲星CC800上摸索的QBASIC。没有人指点,也不知道在哪里获取信息,自己对网络还不太熟悉,纯靠自己摸索。心理琢磨QBASIC可能和BASIC挺像吧,自己不用再看BASIC了吧,直接C语言吧。于是在一整排一整排的C语言的教材中,出现了C++的字眼。我想,既然C比B更好,是不是C++就是C的加强版本啊!?你看,两个加号呢!于是,我捧了两本C++的书回家了。那个夏天,就是我这样没什么像样基础的高中毕业生,开始了自己的C++之旅。
那两本书是我为了学习程序设计而真正付钱购置的书籍,再后来,由于网络上有很多免费资源和盗版书籍资源,大学里也有图书馆,自己从未再为程序设计教材和教程支付过费用。
大学的专业不再是和程序设计有关的了,C++也慢慢淡出了。但是自己不甘,在大四的时候找了一个导师,做了一个和程序设计相关的毕业课题,又能够愉快的写C++了。
但是不知自己是头脑进水还是怎么的,无法追随自己的内心。本科毕业去读研究生,专业又是和程序设计没啥关系。但是自己还是强扭着要在自己的研究领域里做程序设计,此时的主要工具变成了Matlab和C++。C++变的有些生疏了,好些新的特性都不知道,也没有再系统的学习了,用到哪stack overflow到哪。毕业论文就是这样硬生生磨出了几万行C++程序。
经过8年抗战,终于拿到了博士学位,但是此时的我,又面临的人生的抉择:是做自己擅长的,还是做自己喜欢的?是抗争还是妥协?
答案是一句歌词:“不自量力的回首,之至死方休。”
做了那么多年,杂七杂八的事,我还是想写程序啊,此间的少年,还是那个高中毕业后在家苦苦琢磨类和继承的我啊。转了一大圈,发现生活是不能妥协的。
于是,放弃了博士专业擅长的工作,我要去一个能尽情写程序的地方了。
现在我就在这种地方。
于是又开始重新学习,补充C++的知识,练习新的技巧,学习CS的专业课程,一点一点的提高。前日在youtube上看了Bjarne的CPPCON的演讲,推荐他自己写的新书。于是我又一次,自己掏钱买下了这本《A Tour of C++》,虽然电脑上有这本书的PDF,但是我感觉自己不再是那个没有收入的学生了,不能这样随意获取别人的劳动成果,更何况这是创造了我所知道的这一切的人,没有道理不表达我的感激和支持。
这本书是一个很特别的介绍C++的书,字里行间透漏着Bjarne的口吻,大家可以闲来无事看一看,我就是间断的看了大概一个多月。这篇blog就是我自己的一个非常不正规的笔记,我只是记录了我感兴趣和我觉得奇妙的地方,这并不是对书中各个实例的探讨,我想,这也不是一本这样的的书。读这本书就感觉Bjarne像个大师兄,尝试保护我们这些小学弟,不想让我们被一些没有道理而又恐怖的代码吓怯了,想让我们看到C++本原的,简单的,安详的一面。
总之,这是我第三本自己掏钱购买的C++书籍,也值得纪念一下。
2 User-Defined Types
构造函数的Member initializer list 可以用花括号代替圆括号。
enum class:
enum class Color {
red, blue, green };
enum class Traffic_light {
green, yellow, red };
Color color = Color::red;
Color redColor = Traffic_light::red; // Error;
int i = Color::red; // Error.
color = 1; // Error.
Color otherColor {
0}; // OK.
Color anotherColor = Color{
0}; // OK.
这样red
就有了一个scope,不会和其他red
名称混淆。于此同时,enum class是具有类型约束的,不再与整型变量实现自动转换。
3 Modularity
Translation unit: A .cpp file that is compiled by itself (including the h files it #includes) is called a translation unit. A program can consist of many thousand translation units.
C++20: module
目前尚没有变成ISO C++,但是貌似已经可以使用了。
module不同于#include
,module只编译一次,存在于一个translation unit。在cpp中import module的次序不影响被import的module之间的实现。在import 一个module时,不会隐式import module的import。
将一个namespace内的所有名称都至于当前scope:
using namespace std;
只使用一个namespace内的某一个名称:
using std::swap;
swap();
在一个标志了noexcept
的函数内throw,会导致std::terminate()
被调用。
在一个catch块内,throw;
表示再次抛出catch到的异常。
某些其他语言可以通过exception来作为返回值使用,但是C++不推荐这样做,因为在C++的实现中,throw比return要expansive很多。
如何界定一个expected error和一个failure that needs throwing an exception是有些模糊的,作者推荐了一些思路:
对于以下情况,可以设计成抛出异常
- 非常少见的错误,例如printf()函数的错误。
- Immediate caller难以处理的错误,或者是系统级的错误,例如网络异常,内存异常。
- 某些实现转移到了子module,自module中可抛出异常?
- 构造函数发生错误。
- 没有返回值可以用做error code, 或者修改函数接口过于困难。
- 软件设计为有一个集中处理error的结构。此时其他函数都不需要一直检查函数的执行状态。
- recover 依赖于其他函数的执行情况?
- callback函数,原因是调用点只有function abstraction,所以要求调用点对异常进行处理不合适。
- 错误需要“undo”?
使用exception并不一定导致性能下降,但是过多的try会。
assert()
只在debug mode下起作用。
3.6.2 Value Return
Default move constructor 是怎样工作的?
Default move assignment operator 是怎样工作的?
3.6.3 Structured Binding
struct Entry {
string name;
int value;
};
Entry read_entry(istream& is) {
string s;
int i;
is >> s >> i;
return {
s,i};
}
auto [n,v] = read_entry(is);
上述代码中利用return {s, i};
来完成返回值的构造。从函数返回的值可以通过auto [ n, v ]
进行unpack。这种binding也可以用在一些container上
void incr(map<string,int>& m) {
for (auto& [key,value] : m)
++value;
}
3.7 Advice
Don’t put a using-directive in a header file.
4 Classes
4.2.1 An Arithmetic Type
Simple operations must be inlined.
4.2.3 Initializing Containers
可以使用initializer-list constructor来创建container。
4.3 Abstract Types
class Container {
public:
virtual double& operator[](int) = 0; // pure virtual functionvirtual
int size() const = 0;
virtual ~Container() {
}
};
对于所有pure virtual function,子class必须实现这些接口。
Abstract class可以没有构造函数,因为可能没有东西需要初始化。
推荐使用override
关键字,这样能够给complier更多提示以检查潜在的拼写错误或者接口不一致。
4.5.2 Hierarchy Navigation
一般使用dynamic_cast
的场合是,函数接口接收一个base class的指针,传入函数的object又会被该函数返回回来,此时,需要用dynamic_cast
来恢复。
4.5.3 Avoiding Resource Leaks
The code using unique_ptr
will be exactly as efficient as code using the raw pointers correctly.
5. Essential Operations
5.1.1 Essential Operations
如果一个class需要一个non-trivial的析构函数,那么这个类很可能需要定义一整套构造函数和assignment operator。
class X {