Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 5 additions & 7 deletions book/zh-cn/02-usability.md
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,7 @@ int main() {

> 注意,现在大部分编译器其实都带有自身编译优化,很多非法行为在编译器优化的加持下会变得合法,若需重现编译报错的现象需要使用老版本的编译器。

C++11 提供了 `constexpr` 让用户显式的声明函数或对象构造函数在编译器会成为常量表达式,这个关键字明确的告诉编译器应该去验证 `len_foo` 在编译期就应该是一个常量表达式。
C++11 提供了 `constexpr` 让用户显式的声明函数或对象构造函数在编译期会成为常量表达式,这个关键字明确的告诉编译器应该去验证 `len_foo` 在编译期就应该是一个常量表达式。

此外,`constexpr` 的函数可以使用递归:

Expand Down Expand Up @@ -612,8 +612,6 @@ int main() {
}
```

> 直至往后的内容正在更新,尽请期待

### 默认模板参数

我们可能定义了一个加法函数:
Expand All @@ -638,7 +636,7 @@ auto add(T x, U y) -> decltype(x+y) {

### 变长参数模板

模板一直是 C++ 所独有的**黑魔法**(一起念:**Dark Magic**)之一。在 C++11 之前,无论是类模板还是函数模板,都只能按其指定的样子,接受一组固定数量的模板参数;而 C++11 加入了新的表示方法,允许任意个数、任意类别的模板参数,同时也不需要再定义时将参数的个数固定
模板一直是 C++ 所独有的**黑魔法**(一起念:**Dark Magic**)之一。在 C++11 之前,无论是类模板还是函数模板,都只能按其指定的样子,接受一组固定数量的模板参数;而 C++11 加入了新的表示方法,允许任意个数、任意类别的模板参数,同时也不需要在定义时将参数的个数固定

```cpp
template<typename... Ts> class Magic;
Expand Down Expand Up @@ -781,7 +779,7 @@ public:
value1 = 1;
}
Base(int value) : Base() { // 委托 Base() 构造函数
value2 = 2;
value2 = value;
}
};

Expand All @@ -805,7 +803,7 @@ public:
value1 = 1;
}
Base(int value) : Base() { // 委托 Base() 构造函数
value2 = 2;
value2 = value;
}
};
class Subclass : public Base {
Expand Down Expand Up @@ -873,7 +871,7 @@ void foo(); // 非法, foo 已 final

在传统 C++ 中,如果程序员没有提供,编译器会默认为对象生成默认构造函数、复制构造、赋值算符以及析构函数。另外,C++ 也为所有类定义了诸如 `new` `delete` 这样的运算符。当程序员有需要时,可以重载这部分函数。

这就引发了一些需求:无法精确控制默认函数的生成行为。例如禁止类的拷贝时,必须将赋值构造函数与赋值算符声明为 `private`。尝试使用这些未定义的函数将导致编译或链接错误,则是一种非常不优雅的方式。
这就引发了一些需求:无法精确控制默认函数的生成行为。例如禁止类的拷贝时,必须将复制构造函数与赋值算符声明为 `private`。尝试使用这些未定义的函数将导致编译或链接错误,则是一种非常不优雅的方式。

并且,编译器产生的默认构造函数与用户定义的构造函数无法同时存在。若用户定义了任何构造函数,编译器将不再生成默认构造函数,但有时候我们却希望同时拥有这两种构造函数,这就造成了尴尬。

Expand Down
2 changes: 1 addition & 1 deletion book/zh-cn/03-runtime.md
Original file line number Diff line number Diff line change
Expand Up @@ -197,7 +197,7 @@ int main() {

**纯右值\(prvalue, pure rvalue\)**,纯粹的右值,要么是纯粹的字面量,例如 `10`, `true`;要么是求值结果相当于字面量或匿名临时对象,例如 `1+2`。非引用返回的临时变量、运算表达式产生的临时变量、原始字面量、Lambda 表达式都属于纯右值。

**将亡值\(xvalue, expiring value\)**,是 C++11 为了引入右值引用而提出的概念(因此在传统 C++中,纯右值和右值是统一个概念),也就是即将被销毁、却能够被移动的值。
**将亡值\(xvalue, expiring value\)**,是 C++11 为了引入右值引用而提出的概念(因此在传统 C++中,纯右值和右值是同一个概念),也就是即将被销毁、却能够被移动的值。

将亡值可能稍有些难以理解,我们来看这样的代码:

Expand Down
2 changes: 1 addition & 1 deletion book/zh-cn/04-containers.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ order: 4
1. 为什么要引入 `std::array` 而不是直接使用 `std::vector`?
2. 已经有了传统数组,为什么要用 `std::array`?

先回答第一个问题,`std::vector` 太强大了,以至于我们没有必要为了去敲碎一个鸡蛋而用一个钉锤。使用 `std::array` 保存在栈内存中,相比堆内存中的 `std::vector`,我们就能够灵活的访问这里面的元素,从而获得更高的性能;同时正式由于其堆内存存储的特性,有些时候我们还需要自己负责释放这些资源。
先回答第一个问题,`std::vector` 太强大了,以至于我们没有必要为了去敲碎一个鸡蛋而用一个钉锤。使用 `std::array` 保存在栈内存中,相比堆内存中的 `std::vector`,我们就能够灵活的访问这里面的元素,从而获得更高的性能;同时正是由于其堆内存存储的特性,有些时候我们还需要自己负责释放这些资源。

而第二个问题就更加简单,使用`std::array`能够让代码变得更加现代,且封装了一些操作函数,同时还能够友好的使用标准库中的容器算法等等,比如 `std::sort`。

Expand Down
4 changes: 2 additions & 2 deletions book/zh-cn/05-pointers.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,15 +18,15 @@ order: 5

凡事都有例外,我们总会有需要将对象在自由存储上分配的需求,在传统 C++ 里我们只好使用 `new` 和 `delete` 去『记得』对资源进行释放。而 C++11 引入了智能指针的概念,使用了引用计数的想法,让程序员不再需要关心手动释放内存。这些智能指针就包括 `std::shared_ptr`/`std::unique_ptr`/`std::weak_ptr`,使用它们需要包含头文件 `<memory>`。

> 注意:引用计数不是垃圾回收,引用技术能够尽快收回不再被使用的对象,同时在回收的过程中也不会造成长时间的等待,更能够清晰明确的表明资源的生命周期。
> 注意:引用计数不是垃圾回收,引用计数能够尽快收回不再被使用的对象,同时在回收的过程中也不会造成长时间的等待,更能够清晰明确的表明资源的生命周期。

## 5.2 std::shared_ptr

`std::shared_ptr` 是一种智能指针,它能够记录多少个 `shared_ptr` 共同指向一个对象,从而消除显示的调用 `delete`,当引用计数变为零的时候就会将对象自动删除。

但还不够,因为使用 `std::shared_ptr` 仍然需要使用 `new` 来调用,这使得代码出现了某种程度上的不对称。

`std::make_shared` 就能够用来消除显示的使用 `new`,所以`std::make_shared` 会分配创建传入参数中的对象,并返回这个对象类型的`std::shared_ptr`指针。例如:
`std::make_shared` 就能够用来消除显式的使用 `new`,所以`std::make_shared` 会分配创建传入参数中的对象,并返回这个对象类型的`std::shared_ptr`指针。例如:

```cpp
#include <iostream>
Expand Down
2 changes: 1 addition & 1 deletion book/zh-cn/07-thread.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ int main() {

我们在操作系统的相关知识中已经了解过了有关并发技术的基本知识,mutex 就是其中的核心之一。C++11引入了 mutex 相关的类,其所有相关的函数都放在 `<mutex>` 头文件中。

`std::mutex` 是 C++11 中最基本的 `mutex` 类,通过实例化 `std::mutex` 可以创建互斥量,而通过其成员函数 `lock()` 可以仅此能上锁,`unlock()` 可以进行解锁。但是在在实际编写代码的过程中,最好不去直接调用成员函数,因为调用成员函数就需要在每个临界区的出口处调用 `unlock()`,当然,还包括异常。这时候 C++11 还为互斥量提供了一个 RAII 语法的模板类`std::lock_gurad`。RAII 在不失代码简洁性的同时,很好的保证了代码的异常安全性。
`std::mutex` 是 C++11 中最基本的 `mutex` 类,通过实例化 `std::mutex` 可以创建互斥量,而通过其成员函数 `lock()` 可以进行上锁,`unlock()` 可以进行解锁。但是在在实际编写代码的过程中,最好不去直接调用成员函数,因为调用成员函数就需要在每个临界区的出口处调用 `unlock()`,当然,还包括异常。这时候 C++11 还为互斥量提供了一个 RAII 语法的模板类`std::lock_gurad`。RAII 在不失代码简洁性的同时,很好的保证了代码的异常安全性。

在 RAII 用法下,对于临界区的互斥量的创建只需要在作用域的开始部分,例如:

Expand Down