本篇阅读笔记全部整理自知乎in nek开设的软件架构设计专栏。若需详细阅读请移步专栏获得更多资料。另外,全部一级标题均使用原文文章标题,可以直接点击标题链接到原文章进行详细阅读。
从了解到in nek,然后关注他,并且一直阅读他的文章,从中学习了许多做人做事的道理,获益良多。关于软件开发,作者也是深入浅出,娓娓道来,有时甚至有茅塞顿开的感觉。因为作者发布的博文越来越多,知识量也越来越大,故需要个人总结,方能体会得更深刻。
可变参数模板包括可变参数函数模板和可变参数类模板。
可变参数函数模板通常是递归的。第一步调用处理包中的第一个实参,然后用剩余实参调用自身。
在我们的print函数中,每次递归调用将第二个实参打印到第一个实参表示的流中。为了终止递归,我们还需要定义一个非可变参数的print函数,接受一个流和一个对象。
1 | //用来终止递归并打印最后一个元素的函数 |
PROJECT指令隐式定义了两个变量<projectname>_BINARY_DIR和<projectname>_SOURCE_DIR
。同时cmake系统预定义了PROJECT_BINARY_DIR和PROJECT_SOURCE_DIR
两个不依赖项目名的变量,分别表示当前编译发生目录和工程代码目录。为了统一,建议直接使用不依赖工程名的预定义变量。
在cmake中使用${}
引用定义的变量,但在IF控制语句,直接使用变量名引用变量,而不需要${}
。
在cmake语法中,指令是大小写无关的,但变量和参数是大小写相关的。建议指令全部使用大写。
在内部构建中,cmake并没有指令直接全部清除构建过程的中间文件。因此,cmake强烈推荐的是外部构建(out-of-source build)。
在网络编程实践中所用的机器配置如下:
1 | 操作系统 Ubuntu 16.04.2 |
接下来安装编译工具以及相关库等依赖环境,包括cmake、boost、curl、Google Protobuf
:
1 | apt-get install cmake |
安装完成后,boost相关头文件和库文件的位置:
1 | /usr/include/boost |
与小系统相比,大规模编程对程序设计语言的要求更高。大规模应用程序的特殊要求包括:
在独立开发的子系统之间协同处理错误的能力
使用各种库(可能包含独立开发的库)进行协同开发的能力
对比较复杂的应用概念建模的能力
异常处理机制允许程序中独立开发的部分能够在运行时就出现的问题进行通信并作出相应的处理。
异常将问题的检测与解决过程分离开来。程序的一部分负责检测问题的出现,解决问题的任务传递给程序的另一部分。
当抛出一个异常后,栈展开过程沿着嵌套函数的调用链不断查找,直到找到了与异常匹配的catch子句为止,如果没有找到匹配的catch,则它将终止当前程序。
栈展开过程中对象被自动销毁
如果在栈展开过程中退出了某个块,编译器将负责在这个块中创建的对象能被正确的销毁。如果某个局部对象是类类型,则该对象的析构将被自动调用。
如果异常发生在构造函数中,则当前对象可能只是构造了一部分。我们也要确保已构造的成员能被正确的销毁。
析构函数与异常
析构函数总是会被执行的,但是函数中负责释放资源的代码可能因为异常而被跳过。因此,如果我们使用类来控制资源的分配,就能确保无论函数正常结束还是遇到异常,资源都能被正确释放。
析构函数不应该抛出异常,如果析构函数的某个操作可能抛出异常,则该操作应该放置在一个try语句块中,并且在析构函数内部处理。
成员指针(pointer to member)是可以指向类的非静态成员的指针。成员指针指向类的成员,而非类的对象。当初始化一个成员指针时,我们令其指向类的某个成员,但是不指定该成员所属的对象;直到使用成员指针时,才提供成员所属的对象。
1 | class Screen |
数据成员指针必须包含所属的类,因此,我们必须在*之前添加classname::以表示当前定义的指针可以指向包含classname的成员。我们将取地址运算符作用于Screen类的成员而非内存中的一个该类对象。定义数据成员指针方式:
1 | const string Screen::*pdata = &Screen::contents; |
当我们初始化一个成员指针或为成员指针赋值时,该指针并没有指向任何数据。.*和->*
运算符解引用数据成员指针获得该对象的成员:
1 | Screen myScreen, *pScreen = &myScreen; |
内存管理的层级以及对应的特性如下:
1 | void *p1 = malloc(512); //512 bytes |
当我们希望将一些数据组合成单一对象,但又不想麻烦地定义一个新数据结构来表示这些数据时,tuple是非常有用的。我们可以将tuple看作一个“快速而随意”的数据结构。
tuple类型及其伴随类型和函数都定义在tuple头文件中。
1 | tuple<size_t, size_t, size_t> threeD{1, 2, 3}; |
模板是C++中泛型编程的基础。一个模板就是一个创建类或函数的蓝图或者说公式。
除了定义类型参数,还可以在模板中定义非类型参数(nontype parameter)
。一个非类型参数表示一个值而非一个类型。我们通过特定的类型名而非关键字class或typename来指定非类型参数。
当一个模板被实例化时,非类型参数被一个用户提供的或编译器推断出的值所代替。这些值必须是常量表达式,从而允许编译器在编译时实例化模板。非类型参数可以是一个整型,或者是一个指向对象或函数类型的指针或引用。
比较不同长度的字符串字面常量,因此为模板定义了两个非类型的参数,分别表示数组的长度:
1 | template<unsigned N, unsigned M> |
模板程序应该尽量减少对实参类型的要求
当编译器遇到一个模板定义时,它并不生成代码。只有当我们实例化出模板的一个特定版本时,编译器才会生成代码。因此大多数编译错误在实例化期间报告。
模板的头文件通常既包括声明也包括定义
缺失模块。
1、请确保node版本大于6.2
2、在博客根目录(注意不是yilia根目录)执行以下命令:
npm i hexo-generator-json-content --save
3、在根目录_config.yml里添加配置:
jsonContent: meta: false pages: false posts: title: true date: true path: true text: false raw: false content: false slug: false updated: false comments: false link: false permalink: false excerpt: false categories: false tags: true