Skip to content

Commit c8ab478

Browse files
committed
Updated to Chapter 7, Section 3
1 parent fdedec7 commit c8ab478

File tree

16 files changed

+398
-68
lines changed

16 files changed

+398
-68
lines changed

Structure.md

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -472,6 +472,10 @@
472472

473473
解释作用域的概念。
474474

475+
#### 名称查找
476+
477+
既然谈到了作用域,就不得不谈谈名称查找了。
478+
475479
#### 变量的生存期
476480

477481
简单介绍下数据的生存期:自动生存期、静态生存期、线程生存期及动态生存期。
@@ -687,12 +691,6 @@
687691

688692
## 继承中的常见问题
689693

690-
### 同名覆盖问题
691-
692-
#### 变量的同名覆盖
693-
694-
#### 函数的同名覆盖
695-
696694
### 基类指针和基类引用
697695

698696
这一节很重要。

generalized_parts/06_custom_types_and_their_use/04_union.tex

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@ \subsection*{联合体如何组织内存?}
5959
\end{lstlisting}\par
6060
我来解释一下这段代码:我们定义了一个 \lstinline@dynamic@ 类,这个类的对象可能以三种状态存在:整型状态,浮点型状态或字符型状态。\lstinline@type@ 是一个枚举类型,它可以用来标记这个对象当前处于何种状态。\par
6161
而在联合体 \lstinline@Value@ 中,三个成员 \lstinline@vll@, \lstinline@vld@ 和 \lstinline@str@ 分别是整型、浮点型和字符串型。在任何时刻,这个变量只能是这三种类型之一,所以它们不会同时出现,用 \lstinline@union@ 就很合理。\par
62+
需要读者留意的是,在 \lstinline@dynamic@ 中定义 \lstinline@Value@ 的操作不会直接引入一个什么实体。如果我们不用 \lstinline@Value value@ 这句来定义一个 \lstinline@Value@ 类的对象,那么这个内嵌的定义就没有什么存在感了。\par
6263
\subsection*{如何使用联合体?}
6364
那么我们就在主函数中定义一个 \lstinline@dynamic@ 类的对象,并设置它的类型和值。
6465
\begin{lstlisting}

generalized_parts/07_projecting.tex

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,6 @@ \chapter{代码工程}
44
在本章之后,我也会逐步调整示例代码的风格,从新手友好型的 \lstinline@using namespace std@ 逐渐转向通篇 \lstinline@std::@ 的严谨性风格上去。读者也应当学会适应不同风格的代码,并最好形成自己最习惯的一套风格来。\par
55
希望读者能够在学完本章之后掌握一些代码工程的相关知识,这对我们后续的编程大有裨益。\par
66
\import{07_projecting/}{01_separate_compilation.tex}
7-
\import{07_projecting/}{02_scope_and_storage_duration.tex}
8-
\import{07_projecting/}{03_namespace.tex}
7+
\import{07_projecting/}{02_namespace.tex}
8+
\import{07_projecting/}{03_scope_storage_duration_and_linkage.tex}
99
\import{07_projecting/}{04_coding_style.tex}
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
\section{命名空间}
2+
C++为我们提供了丰富的函数、类和对象,我们只需要若干 \lstinline@#include@ 指令就可以把我们需要的功能收入囊中。这很方便,但也可能导致一些问题。笔者有一次在调尝试调用自己写的 \lstinline@swap@ 函数:
3+
\begin{lstlisting}
4+
void swap(int &a, double &b) {
5+
int tmp {a};
6+
a = b;
7+
b = tmp;
8+
}
9+
int main() {
10+
int a {3};
11+
double b {5};
12+
swap(a, b);
13+
cout << a << ' ' << b; //预期输出5 3
14+
}
15+
\end{lstlisting}
16+
这段代码的调试结果合乎预期,而当我把 \lstinline@double b{5}@ 改成 \lstinline@int b{5}@ 的时候,却也输出了同样的内容。\par
17+
但是不应该啊,按理说 \lstinline@int@ 类型的 \lstinline@b@ 会转换成 \lstinline@double@ 类型的临时变量,但这个临时变量是右值\footnote{我们将在精讲篇谈左值/右值的问题。},不可能被 \lstinline@swap@ 接收。为什么这个函数仍然正常运行并且给出了``正确''的输出呢?\par
18+
再深入研究之后我才发现,原来程序很本就没有调用我自定义的 \lstinline@swap@ 函数,它调用的是标准库自带的 \lstinline@swap@!
19+
\begin{lstlisting}
20+
template< class T >
21+
void swap( T& a, T& b ) noexcept( /* ... */ );
22+
\end{lstlisting}
23+
也就是从这个时期起,我逐渐改掉了使用 \lstinline@using namespace std@ 和 \lstinline@#include<bits/stdc++.h>@\footnote{\lstinline@bits/stdc++.h@ 是GCC编译器中允许的一个不标准的头文件。使用这个头文件可以让我们一次性包含许多常见头文件。这种用法被普遍认为是``不好的习惯'',详见\href{https://stackoverflow.com/questions/31816095/why-should-i-not-include-bits-stdc-h}{Why should I not \#include <bits/stdc++.h>? - Stack Overflow}。}这两个习惯。\par
24+
几乎所有初学者都会无一例外地认为 \lstinline@using namespace std@ 是很方便的;而通篇累牍的 \lstinline@std::cin@ 和 \lstinline@std::cout@ 则显得十分麻烦。但是当我们的代码量非常大之后,我们难免会起一些与标准库中名字相冲突的变量、函数、枚举、结构和类。举几个例子吧:
25+
\begin{itemize}
26+
\item \lstinline@list@, \lstinline@map@, \lstinline@array@, \lstinline@queue@, \lstinline@set@, \lstinline@string@, \lstinline@pair@;
27+
\item \lstinline@copy@, \lstinline@find@, \lstinline@move@, \lstinline@search@, \lstinline@count@, \lstinline@sample@;
28+
\item \lstinline@next@, \lstinline@begin@, \lstinline@end@, \lstinline@data@, \lstinline@size@;
29+
\item \lstinline@function@, \lstinline@future@, \lstinline@thread@, \lstinline@yield@;
30+
\item \ldots\ldots
31+
\end{itemize}
32+
以上这些,有些是类模版,有些是函数模版,或者还有别的。这些名字你可能未必都知道,但是总会有些单词是你熟悉的吧!万一某天你在发愁要起什么名字的时候突然想到了它们——那可能就是问题的根源了。\par
33+
试想,当你在用这些名字定义函数的时候,你是在重载它们;而当你在用这些名字定义类的时候,你会被编译器禁止(因为类不能重载)。被编译器禁止还是更好的选择,因为它在编译期就能帮你检查出错误来;但如果把这些错误留到运行期,那就很难说会发生什么了!\par
34+
解决这个问题的方法很多样。我们可以自己有意识地避开这些名字(一般来说,只要你经验丰富,就不太容易在这个问题上翻车);或者干脆点,不用 \lstinline@using namespace std@ 了\footnote{不过我并不建议读者彻底杜绝 \lstinline@using namespace std@ 的使用。这种语法并不像 \lstinline@bits/stdc++.h@ 库或 \lstinline@goto@ 语句那样饱受诟病,仍然是许多程序员图省事的最佳选择。}。那么究竟什么是 \lstinline@namespace@,我们为什么要用它,又怎么用它呢?本节就来讲解这些问题。\par
35+
\subsection*{什么是命名空间?}
36+
想象一下Windows电脑的文件资源管理器——就以D盘为例吧。我们可以直接把文件存放在D盘的主目录下,不过我们很少这么做;我们绝大多数时候是在D盘中建立一些文件夹,甚至是文件夹套文件夹,然后把具体的文件存放到这些文件夹中。简化起见,我们只在D盘中用两个文件夹。这两个文件夹中各有一份名叫\texttt{Test.txt}的文本文档\footnote{它们二者之间不是快捷方式的关系,是真正意义上独立的文件。否则这个讨论就没什么意义了。}。它们虽然同名,但它们不冲突,因为它们在不同的文件夹下。修改其中一个\texttt{Test.txt}中的内容也不会对另一个\texttt{Test.txt}造成什么影响。\par
37+
那么我们是不是可以这样认为:这两个文件虽然重名,但它们存在于不同的空间中,所以不会有什么冲突。如果我们想访问这两个文件,我们也肯定会通过不同路径来找。这样,一切可能的冲突都解决了。\par
38+
\begin{figure}[htbp]
39+
\centering
40+
\includegraphics[width=0.5\textwidth]{../images/generalized_parts/07_file_explorer_and_namespaces_300.png}
41+
\caption{Windows文件资源管理器中的重名文件}
42+
\end{figure}
43+
命名空间也是这样的道理。在不同的命名空间中,我们可以按我们的需要来给变量、函数和类起名字,而不必担心与其它命名空间中的什么东西重名。\par
44+
不过,实际上的命名空间还是与Windows资源管理器的结构不同的。接下来我们讲解一些有关命名空间的最基本规则。\par
45+
\subsection*{命名空间的层级}

generalized_parts/07_projecting/02_scope_and_storage_duration.tex

Whitespace-only changes.

generalized_parts/07_projecting/03_namespace.tex

Whitespace-only changes.

0 commit comments

Comments
 (0)