book: fix a chunk of english typos

Fixes #87
This commit is contained in:
Changkun Ou
2020-03-15 15:30:48 +01:00
parent 89060d4b96
commit a5d1563609
12 changed files with 34 additions and 32 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

View File

@@ -11,7 +11,7 @@ order: 0
## Introduction ## Introduction
C++ user group is a fairly large. From the advent of C++98 to the official finalization of C++11, it has accumulated over a decade. C++14/17 is an important complement and optimization for C++11, and C++20 brings this language to the door of modernization. The extended features of all these new standards are given to the C++ language. Infused with new vitality. C++ user group is a fairly large. From the advent of C++98 to the official finalization of C++11, it has accumulated over a decade. C++14/17 is an important complement and optimization for C++11, and C++20 brings this language to the door of modernization. The extended features of all these new standards are given to the C++ language. Infused with new vitality.
C++ programmers, who are still using **traditional C++** (this book refers to C++98 and its previous C++ standards as traditional C++), may even amzed by the fact that they are not using the same language while reading modern C++ code. C++ programmers, who are still using **traditional C++** (this book refers to C++98 and its previous C++ standards as traditional C++), may even amazed by the fact that they are not using the same language while reading modern C++ code.
**Modern C++** (this book refers to C++11/14/17/20) introduces a lot of features into traditional C++, which makes the whole C++ become language that modernized. Modern C++ not only enhances the usability of the C++ language itself, but the modification of the `auto` keyword semantics gives us more confidence in manipulating extremely complex template types. At the same time, a lot of enhancements have been made to the language runtime. The emergence of Lambda expressions has made C++ have the "closure" feature of "anonymous functions", which is almost in modern programming languages (such as Python/Swift/.. It has become commonplace, and the emergence of rvalue references has solved the problem of temporary object efficiency that C++ has long been criticized. **Modern C++** (this book refers to C++11/14/17/20) introduces a lot of features into traditional C++, which makes the whole C++ become language that modernized. Modern C++ not only enhances the usability of the C++ language itself, but the modification of the `auto` keyword semantics gives us more confidence in manipulating extremely complex template types. At the same time, a lot of enhancements have been made to the language runtime. The emergence of Lambda expressions has made C++ have the "closure" feature of "anonymous functions", which is almost in modern programming languages (such as Python/Swift/.. It has become commonplace, and the emergence of rvalue references has solved the problem of temporary object efficiency that C++ has long been criticized.

View File

@@ -23,9 +23,10 @@ InstalledDir: /Library/Developer/CommandLineTools/usr/bin
Before learning modern C++, let's take a look at the main features that have been deprecated since C++11: Before learning modern C++, let's take a look at the main features that have been deprecated since C++11:
> **Note**: Deprecation is not completely unusable, it is only intended to imply that programmers will disappear from future standards and should be avoided. However, the deprecated features are still part of the standard library, and most of the features are actually "permanently" reserved for compatibility reasons. > **Note**: Deprecation is not completely unusable, it is only intended to imply that features will disappear from future standards and should be avoided. However, the deprecated features are still part of the standard library, and most of the features are actually "permanently" reserved for compatibility reasons.
- **The string literal constant is no longer allowed to be assigned to a `char *`. If you need to assign and initialize a `char *` with a string literal constant, you should use `const char *` or `auto`.** - **The string literal constant is no longer allowed to be assigned to a `char *`. If you need to assign and initialize a `char *` with a string literal constant, you should use `const char *` or `auto`.**
```cpp ```cpp
char *str = "hello world!"; // A deprecation warning will appear char *str = "hello world!"; // A deprecation warning will appear
``` ```
@@ -115,6 +116,7 @@ LDFLAGS_COMMON = -std=c++2a
all: all:
$(C) -c $(SOURCE_C) $(C) -c $(SOURCE_C)
$(CXX) $(SOURCE_CXX) $(OBJECTS_C) $(LDFLAGS_COMMON) -o $(TARGET) $(CXX) $(SOURCE_CXX) $(OBJECTS_C) $(LDFLAGS_COMMON) -o $(TARGET)
clean: clean:
rm -rf *.o $(TARGET) rm -rf *.o $(TARGET)
``` ```

View File

@@ -143,7 +143,7 @@ In the above example, `char arr_4[len_2]` may be confusing because `len_2` has b
Why is `char arr_4[len_2]` still illegal? Why is `char arr_4[len_2]` still illegal?
This is because the length of the array in the C++ standard must be a constant expression, This is because the length of the array in the C++ standard must be a constant expression,
and for `len_2`, this is a `const` constant, not a constant expression, and for `len_2`, this is a `const` constant, not a constant expression,
so even if this behavior is in most compilers Both support, but) it is an illegal behavior, so even if this behavior is supported by most compilers, but it is an illegal behavior,
we need to use the `constexpr` feature introduced in C++11, which will be introduced next, we need to use the `constexpr` feature introduced in C++11, which will be introduced next,
to solve this problem; for `arr_5`, before C++98 The compiler cannot know that `len_foo()` to solve this problem; for `arr_5`, before C++98 The compiler cannot know that `len_foo()`
actually returns a constant at runtime, which causes illegal production. actually returns a constant at runtime, which causes illegal production.
@@ -155,7 +155,7 @@ actually returns a constant at runtime, which causes illegal production.
C++11 provides `constexpr` to let the user explicitly declare that the function or C++11 provides `constexpr` to let the user explicitly declare that the function or
object constructor will become a constant expression at compile time. object constructor will become a constant expression at compile time.
This keyword explicitly tells the compiler that it should verify that `len_foo` This keyword explicitly tells the compiler that it should verify that `len_foo`
should be a compile time. Constant expression. should be a compile time constant expression. Constant expression.
In addition, the function of `constexpr` can use recursion: In addition, the function of `constexpr` can use recursion:
@@ -222,8 +222,8 @@ int main() {
``` ```
In the above code, we can see that the `itr` variable is defined in the scope of In the above code, we can see that the `itr` variable is defined in the scope of
the entire `main()`, which causes us to rename the other when we need to traverse the entire `main()`, which causes us to rename the other when a variable need to traverse
the entire `std::vectors` again. A variable. C++17 eliminates this limitation so that the entire `std::vectors` again. C++17 eliminates this limitation so that
we can do this in if(or switch): we can do this in if(or switch):
```cpp ```cpp
@@ -280,7 +280,7 @@ To solve this problem,
C++11 first binds the concept of the initialization list to the type C++11 first binds the concept of the initialization list to the type
and calls it `std::initializer_list`, and calls it `std::initializer_list`,
allowing the constructor or other function to use the initialization list allowing the constructor or other function to use the initialization list
like a parameter, which is The initialization of class objects provides like a parameter, which is the initialization of class objects provides
a unified bridge between normal arrays and POD initialization methods, a unified bridge between normal arrays and POD initialization methods,
such as: such as:
@@ -961,7 +961,7 @@ public:
}; };
class Subclass : public Base { class Subclass : public Base {
public: public:
using Base::Base; // inhereit constructor using Base::Base; // inheritance constructor
}; };
int main() { int main() {
Subclass s(3); Subclass s(3);

View File

@@ -11,7 +11,7 @@ order: 3
## 3.1 Lambda Expression ## 3.1 Lambda Expression
Lambda expressions are one of the most important features in modern C++, and Lambda expressions actually provide a feature like anonymous functions. Lambda expressions are one of the most important features in modern C++, and Lambda expressions actually provide a feature like anonymous functions.
Anonymous functions are used when a function is needed, but you don't want to use a function to name a function. There are actually many, many scenes like this. Anonymous functions are used when a function is needed, but you dont want to use name to call a function. There are actually many, many scenes like this.
So anonymous functions are almost standard on modern programming languages. So anonymous functions are almost standard on modern programming languages.
### Basics ### Basics
@@ -24,7 +24,7 @@ The basic syntax of a Lambda expression is as follows:
} }
``` ```
The above grammar rules are well understood except for the things in `[catch list]`, The above grammar rules are well understood except for the things in `[capture list]`,
except that the function name of the general function is omitted. except that the function name of the general function is omitted.
The return value is in the form of a `->` The return value is in the form of a `->`
(we have already mentioned this in the tail return type earlier in the previous section). (we have already mentioned this in the tail return type earlier in the previous section).
@@ -143,7 +143,7 @@ void lambda_generic() {
## 3.2 Function Object Wrapper ## 3.2 Function Object Wrapper
Although this part of the standard library is part of the standard library, Although the features are part of the standard library and not found in runtime,
it enhances the runtime capabilities of the C++ language. it enhances the runtime capabilities of the C++ language.
This part of the content is also very important, so put it here for introduction. This part of the content is also very important, so put it here for introduction.
@@ -262,7 +262,7 @@ are all pure rvalue values.
**xvalue, expiring value** is the concept proposed by C++11 to introduce **xvalue, expiring value** is the concept proposed by C++11 to introduce
rvalue references (so in traditional C++, pure rvalue and rvalue are the same concept), rvalue references (so in traditional C++, pure rvalue and rvalue are the same concept),
that is, A value that is destroyed but can be moved. a value that is destroyed but can be moved.
It would be a little hard to understand the xvalue, It would be a little hard to understand the xvalue,
let's look at the code like this: let's look at the code like this:
@@ -330,7 +330,7 @@ int main()
std::string&& rv2 = lv1 + lv2; // legal, rvalue ref extend lifecycle std::string&& rv2 = lv1 + lv2; // legal, rvalue ref extend lifecycle
rv2 += "string"; // legal, non-const reference can be modified rv2 += "string"; // legal, non-const reference can be modified
std::cout << rv2 << std::endl; // string,string,string, std::cout << rv2 << std::endl; // string,string,string,string
reference(rv2); // output: lvalue reference(rv2); // output: lvalue
@@ -496,7 +496,7 @@ For `pass(1)`, although the value is the rvalue, since `v` is a reference, it is
Therefore `reference(v)` will call `reference(int&)` and output lvalue. Therefore `reference(v)` will call `reference(int&)` and output lvalue.
For `pass(l)`, `l` is an lvalue, why is it successfully passed to `pass(T&&)`? For `pass(l)`, `l` is an lvalue, why is it successfully passed to `pass(T&&)`?
This is based on the **reference contraction rule**: In traditional C++, we are not able to continue to reference a reference type. This is based on the **reference collapsing rule**: In traditional C++, we are not able to continue to reference a reference type.
However, However,
C++ has relaxed this practice with the advent of rvalue references, C++ has relaxed this practice with the advent of rvalue references,
resulting in a reference collapse rule that allows us to reference references, resulting in a reference collapse rule that allows us to reference references,

View File

@@ -172,7 +172,7 @@ Key:[3] Value:[3]
## 4.3 Tuples ## 4.3 Tuples
Programmers who have known Python should be aware of the concept of tuples. Looking at the containers in traditional C++, except for `std::pair` Programmers who have known Python should be aware of the concept of tuples. Looking at the containers in traditional C++, except for `std::pair`
There seems to be no ready-made structure to store different types of data (usually we will define the structure ourselves). there seems to be no ready-made structure to store different types of data (usually we will define the structure ourselves).
But the flaw of `std::pair` is obvious, only two elements can be saved. But the flaw of `std::pair` is obvious, only two elements can be saved.
### Basic Operations ### Basic Operations

View File

@@ -26,7 +26,7 @@ These smart pointers include `std::shared_ptr`/`std::unique_ptr`/`std::weak_ptr`
## 5.2 `std::shared_ptr` ## 5.2 `std::shared_ptr`
`std::shared_ptr` is a smart pointer that records how many `shared_ptr` points to an object, eliminating the display call `delete`, which automatically deletes the object when the reference count becomes zero. `std::shared_ptr` is a smart pointer that records how many `shared_ptr` points to an object, eliminating to call `delete`, which automatically deletes the object when the reference count becomes zero.
But not enough, because using `std::shared_ptr` still needs to be called with `new`, which makes the code a certain degree of asymmetry. But not enough, because using `std::shared_ptr` still needs to be called with `new`, which makes the code a certain degree of asymmetry.
@@ -133,12 +133,12 @@ int main() {
// p2 is empty, no prints // p2 is empty, no prints
if(p2) p2->foo(); if(p2) p2->foo();
std::cout << "p2 was destroied" << std::endl; std::cout << "p2 was destroyed" << std::endl;
} }
// p1 is not empty, prints // p1 is not empty, prints
if (p1) p1->foo(); if (p1) p1->foo();
// Foo instance will be destroied when leaving the scope // Foo instance will be destroyed when leaving the scope
} }
``` ```
@@ -157,14 +157,14 @@ class A {
public: public:
std::shared_ptr<B> pointer; std::shared_ptr<B> pointer;
~A() { ~A() {
std::cout << "A was destroied" << std::endl; std::cout << "A was destroyed" << std::endl;
} }
}; };
class B { class B {
public: public:
std::shared_ptr<A> pointer; std::shared_ptr<A> pointer;
~B() { ~B() {
std::cout << "B was destroied" << std::endl; std::cout << "B was destroyed" << std::endl;
} }
}; };
int main() { int main() {
@@ -177,9 +177,9 @@ int main() {
} }
``` ```
The result is that A and B will not be destroyed. This is because the pointer inside a, b also references `a, b`, which makes the reference count of `a, b` become 2, leaving the scope. When the `a, b` smart pointer is destructed, it can only cause the reference count of this area to be decremented by one. This causes the memory area reference count pointed to by the `a, b` object to be non-zero, but the external has no The way to find this area, it also caused a memory leak, as shown in Figure 5.1: The result is that A and B will not be destroyed. This is because the pointer inside a, b also references `a, b`, which makes the reference count of `a, b` become 2, leaving the scope. When the `a, b` smart pointer is destructed, it can only cause the reference count of this area to be decremented by one. This causes the memory area reference count pointed to by the `a, b` object to be non-zero, but the external has no way to find this area, it also caused a memory leak, as shown in Figure 5.1:
![Figure 5.1](../../assets/figures/pointers1.png) ![Figure 5.1](../../assets/figures/pointers1_en.png)
The solution to this problem is to use the weak reference pointer `std::weak_ptr`, which is a weak reference (compared to `std::shared_ptr` is a strong reference). A weak reference does not cause an increase in the reference count. When a weak reference is used, the final release process is shown in Figure 5.2: The solution to this problem is to use the weak reference pointer `std::weak_ptr`, which is a weak reference (compared to `std::shared_ptr` is a strong reference). A weak reference does not cause an increase in the reference count. When a weak reference is used, the final release process is shown in Figure 5.2:

View File

@@ -35,9 +35,9 @@ C++11 introduces a class related to `mutex`, with all related functions in the `
It can be locked by its member function `lock()`, and `unlock()` can be unlocked. It can be locked by its member function `lock()`, and `unlock()` can be unlocked.
But in the process of actually writing the code, it is best not to directly call the member function, But in the process of actually writing the code, it is best not to directly call the member function,
Because calling member functions, you need to call `unlock()` at the exit of each critical section, and of course, exceptions. Because calling member functions, you need to call `unlock()` at the exit of each critical section, and of course, exceptions.
At this time, C++11 also provides a template class `std::lock_gurad` for the RAII syntax for the mutex. At this time, C++11 also provides a template class `std::lock_guard` for the RAII syntax for the mutex.
RAII guarantees the exceptional security of the code while losing the simplicity of the code. RAII guarantees the exceptional security of the code while keeping the simplicity of the code.
```cpp ```cpp
#include <iostream> #include <iostream>
@@ -156,7 +156,7 @@ The condition variable `std::condition_variable` was born to solve the deadlock
For example, a thread may need to wait for a condition to be true to continue execution. For example, a thread may need to wait for a condition to be true to continue execution.
A dead wait loop can cause all other threads to fail to enter the critical section so that when the condition is true, a deadlock occurs. A dead wait loop can cause all other threads to fail to enter the critical section so that when the condition is true, a deadlock occurs.
Therefore, the `condition_variable` instance is created primarily to wake up the waiting thread and avoid deadlocks. Therefore, the `condition_variable` instance is created primarily to wake up the waiting thread and avoid deadlocks.
`notd_one()` of `std::condition_variable` is used to wake up a thread; `notify_one()` of `std::condition_variable` is used to wake up a thread;
`notify_all()` is to notify all threads. Below is an example of a producer and consumer model: `notify_all()` is to notify all threads. Below is an example of a producer and consumer model:
```cpp ```cpp
@@ -253,7 +253,7 @@ int main() {
} }
``` ```
Intuitively, ʻa = 5;` in `t2` seems to always execute before `flag = 1;`, and `while (flag != 1)` in `t1` seems to guarantee `std ::cout << "b = " << b << std::endl;` will not be executed before the mark is changed. Logically, it seems that the value of `b` should be equal to 5. Intuitively, `a = 5;` seems in `t2` seems to always execute before `flag = 1;`, and `while (flag != 1)` in `t1` seems to guarantee `std ::cout << "b = " << b << std::endl;` will not be executed before the mark is changed. Logically, it seems that the value of `b` should be equal to 5.
But the actual situation is much more complicated than this, or the code itself is undefined behavior, because for `a` and `flag`, they are read and written in two parallel threads. But the actual situation is much more complicated than this, or the code itself is undefined behavior, because for `a` and `flag`, they are read and written in two parallel threads.
There has been competition. In addition, even if we ignore competing reading and writing, it is still possible to receive out-of-order execution of the CPU, and the impact of the compiler on the rearrangement of instructions. There has been competition. In addition, even if we ignore competing reading and writing, it is still possible to receive out-of-order execution of the CPU, and the impact of the compiler on the rearrangement of instructions.
Cause `a = 5` to occur after `flag = 1`. Thus `b` may output 0. Cause `a = 5` to occur after `flag = 1`. Thus `b` may output 0.

View File

@@ -35,7 +35,7 @@ C++11 引入了 `mutex` 相关的类,其所有相关的函数都放在 `<mutex
而通过其成员函数 `lock()` 可以进行上锁,`unlock()` 可以进行解锁。 而通过其成员函数 `lock()` 可以进行上锁,`unlock()` 可以进行解锁。
但是在在实际编写代码的过程中,最好不去直接调用成员函数, 但是在在实际编写代码的过程中,最好不去直接调用成员函数,
因为调用成员函数就需要在每个临界区的出口处调用 `unlock()`,当然,还包括异常。 因为调用成员函数就需要在每个临界区的出口处调用 `unlock()`,当然,还包括异常。
这时候 C++11 还为互斥量提供了一个 RAII 语法的模板类 `std::lock_gurad` 这时候 C++11 还为互斥量提供了一个 RAII 语法的模板类 `std::lock_guard`
RAII 在不失代码简洁性的同时,很好的保证了代码的异常安全性。 RAII 在不失代码简洁性的同时,很好的保证了代码的异常安全性。
在 RAII 用法下,对于临界区的互斥量的创建只需要在作用域的开始部分,例如: 在 RAII 用法下,对于临界区的互斥量的创建只需要在作用域的开始部分,例如:

View File

@@ -41,10 +41,10 @@ int main() {
// p2 is empty, no prints // p2 is empty, no prints
if(p2) p2->foo(); if(p2) p2->foo();
std::cout << "p2 was destroied" << std::endl; std::cout << "p2 was destroyed" << std::endl;
} }
// p1 is not empty, prints // p1 is not empty, prints
if (p1) p1->foo(); if (p1) p1->foo();
// Foo instance will be destroied when leaving the scope // Foo instance will be destroyed when leaving the scope
} }

View File

@@ -18,14 +18,14 @@ class A {
public: public:
std::shared_ptr<B> pointer; std::shared_ptr<B> pointer;
~A() { ~A() {
std::cout << "A was destroied" << std::endl; std::cout << "A was destroyed" << std::endl;
} }
}; };
class B { class B {
public: public:
std::shared_ptr<A> pointer; std::shared_ptr<A> pointer;
~B() { ~B() {
std::cout << "B was destroied" << std::endl; std::cout << "B was destroyed" << std::endl;
} }
}; };
int main() { int main() {

View File

@@ -109,7 +109,7 @@ decltype(auto) ThreadPool::enqueue(F&& f, Args&&... args) {
{ {
std::unique_lock<std::mutex> lock(queue_mutex); std::unique_lock<std::mutex> lock(queue_mutex);
// avoid add new thread if theadpool is destroied // avoid add new thread if theadpool is destroyed
if(stop) if(stop)
throw std::runtime_error("enqueue on stopped ThreadPool"); throw std::runtime_error("enqueue on stopped ThreadPool");