C++编译链接模型笔记
更新日期:
第一节
- C++语言的三大约束是:与C兼容,零开销,值语义。
- 与C兼容:不仅是语法兼容,更重要的是兼容C语言的编译模型与运行模型,也就是能直接使用
C语言的头文件。 - 编译的步骤:preprocessor/complier/assembler/linker
- include的缺点: 让编译效率降低。留下安全隐患,头文件有传递性,可能引入不必要的依赖。
- gcc -E *.cc 预处理
第二节
- 单遍编译,C++继承了C的单遍编译,但是影响了名字查找和函数重载决议。
- 所谓单遍编译,指的是从头到尾扫描一遍源码,一边解析源码,一边生成目标代码,也就是编译时,只能看到
目前已经解析过的源码,看不到后面的源码,也是C语言中需要函数声明的原因。结构体必须先定义。 - AA BB(CC);这句话既可以声明函数,也可以定义变量。
- C++ 只能通过解析源码来了解名字的含义。意味着要准确理解一行代码的意义,需要通读之前的所有代码。
- 函数重载决议,当C++编译器读到一个函数调用时,它必须从已经看到的同名函数中选出最佳的函数,哪怕后面
出现了更合适的匹配。 - 前向声明
- 在.h文件里声明函数,在.CC文件里实现函数,但是有缺陷,即一样的东西区分声明和定义,代码放在
不同的文件中,这就有了出错的可能,比如在一个源文件里声明 extern char *name,但是到了另一个源文件里成了
char name[] = “chen shuo”;. - 对于函数而言,这种不一致表现在参数列表和返回类型上。
- 对于 class Foo 以下几种情况不需要看见其完整的定义:1,定义或声明Foo*和Foo&,包括用于函数参数,返回类型
,局部变量,类成员变量等。2,声明一个Foo为参数或返回类型的函数,如 Foo bar(),或者 void bar(Foo f),但是如
果代码里调用这个函数就需要知道Foo的定义,因为编译器要知道Foo的拷贝构造函数和析构函数。 - 不能重载operator&,因为一旦重载,这个class就不能用前向声明了。
第三节 链接
重载
- 为了实现函数重载,c++编译器采用名字改编的办法,为每个重载函数生成独一无二的名字,这样链接的时候就能找
到正确的函数。 - 返回类型不参与函数重载。
inline
- 重复代码消除:如果编译器如法inline展开的话,每个编译单元都会生成inline函数的目标代码,然后linker
会从多分实现中任选一份保留,其余的则丢弃(vague linkage)。 - 如何判断一个C++可执行文件是debug build 还是 release build?通常的做法是判断class template 的短函数
成员有没有被inline展开。 - g++ -Wall *.cc 没有优化的bulid,不会inline展开。debug build.
- g++ -Wall -o2 *.cc inline展开。release build.
- 编译器自动生成的析构函数也是inline的
模板,虚函数
- 模板的定义一般要放到头文件中,否则会有编译错误,但是所谓的编译错误只是链接错误,有办法将模板的实现
放到 *.cc文件里,头文件里只放声明,前提是你知道模板会有那些具体化类型,并事先显式具体化出来。如:
template
T bar(const T& x)
{
/….
}
template char bar(const char&);// *.cc 具体化 - 虚函数
- 虚函数的调用是通过虚函数表(vtable)进行的,每个多态的class都应该有一份vtable。定义或继承了虚函数的
对象中会有一个隐含的成员:指向vtable的指针,即vptr。在析构或者构造对象的时候,编译器会修改vptr成员。
第四节 头文件的使用规则
- 头文件的害处:
- 传递性。头文件还可以再包含其他头文件,一方面造成编译缓慢,另一方面,任何一个头文件改动一点点都会造成
重新编译。 - 顺序性。一个源文件可以包含多个头文件,如果头文件组织不当,会造成程序的语义跟头文件的包含顺序有关。
- 差异性。内容差异造成不同源文件看到的头文件不一致,时间差异造成头文件与库文件的内容不一致。
- 改动程序本身或它的依赖库之后,应该重新测试。
- Linux的共享库的优点:1,一致的内存管理,Linux共享库与应用程序共享一个heap,因此动态库分配的内存可以
交给应用程序去释放。2,一致的初始化,动态库里的静态对象的初始化和程序其他地方的静态对象一样。3,在动态库
中可以放心使用class, STL。4, DLL Hell的问题也少的多。 - Dll Hell指的是安装新的软件是更新了某个公用的DLL,破坏了其他已有的软件的功能。
- 必须保证应用程序之间的独立性,也就是让动态库的多个版本可以共存。
- 静态库的作者把源文件编译成.a库文件,连同头文件一起打包发布,应用程序使用这个静态库,但是编译库文件
比编译可执行文件要早,这就可能造成编译应用程序时看到的头文件与编译库文件时看到的不一样。