从源文件到可执行文件
- 预处理(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函数。即增强了功能,又没有破坏封装性,由没有增加编译依赖。
敲黑板两个角度:
- 增强封装性
- 减小编译依赖