从源文件到可执行文件
- 预处理(pre-processing)E
从.c到.i。编译器将C源代码中的包含的头文件如stdio.h编译进来,替换宏。 - 编译(Compiling)S
从.i到.s。gcc首先要检查代码的规范性、是否有语法错误等,以确定代码的实际要做的工作,在检查无误后,gcc把代码翻译成汇编语言。 - 汇编(Assembling) c
把编译阶段生成的.s文件转成二进制目标代码.o文件。 - 链接 (Linking)
链接到库中,生成可执行文件。
绘制这里的图(https://blog.csdn.net/Meteor_s/article/details/85208589)
这里更详细

1 | gcc -E hello.c -o hello.i |
生成.o文件,可以重定向,方便其他应用使用。
一条命令从源文件到可执行文件:
1 | gcc hello.c –o hello |
1 虚析构函数
补充内容(tip 7):
任何时候都应该为待多态性质的基类(父类)声明virtual析构函数。如果一个class含有任何virual函数,就一定要有一个virtual析构函数。
而如果这个class的设计不是作为基类使用,或者不准备让这个class具备多态性,就不该声明virtual析构函数。
含有多态性质的base class的设计目的是为了通过base class的接口来使用derived class。所以通常使用base class类的指针指向derived class的对象。
并不是所有的base class都是为了多态用途,比如STL容器的设计不是用作base class的。
virtual同名函数的目的是允许derived class的实现可以客制化。
2 C++帮你实现了的函数
(tip 5)
当定义了一个class,又没有实现任何成员函数时,编译器会为这个class声明4个函数:
- 一个default构造函数
- 一个copy构造函数
- 一个析构函数
- 一个copy操作符
这些函数都是public和inline的。如下:
1 | class Myclass{ |
其中
- 编译器给出的析构函数是个
non-virtual函数,除非这个class的base class含有virtual析构函数。此时这个class的虚拟性来自其base class。 - 对于构造函数,如果我声明了一个构造函数,那么编译器不再创建
default构造函数 - copy构造函数,和copy操作符只是单纯的将来源对象的每一个
non-static成员变量拷贝到目标对象。所以:是使用1
Myclass e2(e1)
e1的成员变量来初始化e2的成员变量。
3 为classes实现赋值操作符
使用赋值操作时,通常可以写成如下形式:
1 | int x, y, z; |
上述实际上的赋值行为是
1 | x=(y=(z=2)); |
其行为是将2赋值给z,再将z赋值给y,最后将y赋值给x。
为了实现上述的连锁赋值,赋值操作符必须返回一个引用指向操作符的左侧:
1 | class Myclass{ |
同样的上述的形式也适用于所有相关的操作符,如+=,-=,×=。
这是一条协议,被所有内置类型和标准库函数共同遵守,所以,遵守它吧。
4 将成员变量声明为private
为什么要把classes的成员变量声明为private:因为这体现了封装。
类成员变量应该只被这个类的成员方法可见,protected成员变量同public成员变量一样缺乏封装性。
从封装的角度讲,访问权限只有提供封装(private)和不提供封装两种。
5 使用non-member non-friend函数而非再定义一个member函数
假设由一个类含有若干个清理函数,只是清理的对象不同:
1 | class WebBrowser{ |
其实很多时候,用户想要一次性执行这些动作,如何一次性完成,两种方案:
给这个class中添加一个member函数
clearAll(),它调用上述三个清理函数:1
2
3
4
5
6class WebBrowser{
public:
...
void clearAll();
...
};使用一个non-member,non-friend函数:
1
2
3
4
5void clearBrowser(WebBrowser& wb){
wb.clearHistory();
wb.clearCache();
wb.clearCookies();
}pro tip告诉你使用后者。后者更体系那封装性。后者保护了类的包裹性,进而有较低的编译依赖,增强了类的可延展性。
对于封装性,可以访问private成员变量的只有member函数和friend函数。上述两种方式提供了相同的功能,而后者提供了较强的封装性,因为它不能增加访问private变量的能力。
P.S. friend函数和member函数对于类private成员的访问能力相同。
在C++ 中自然的做法是将类WebBrowser和函数clearBrowser()放置于同一个namespace中:
1 | namespace Web{ |
namespace 可以跨越多个源码文件,而class不能。就是说相同的namespace中可以有多个头文件,这正是C++标准库的组织方式:std::vector, std::sort, std::map, … C++ 将不同的部分,不同container放在不同的头文件中,每个声明了std的某部分功能,这样当使用vector,时只用include <vector>,而不必将所有的std内容include进来。就是说,用户只用对所用的部分进行编译。而class必须整体定义,不能分割。
想要扩充这个namespace的能力,只需要在这个namespace中添加更多的non-member,non-friend函数。即增强了功能,又没有破坏封装性,由没有增加编译依赖。
敲黑板两个角度:
- 增强封装性
- 减小编译依赖