C++的一些语言特性使之必须和编译器链接器共同支持才能工作。
- 重复代码消除
- 全局构造和析构
重复代码消除:
C++编译器在很多时候会产生重复的代码,比如模板(Templates)、外部内联函数和虚函数表(Virtual Function Table)都有可能在不同的编译单元里生成相同的代码。最简单的情况就拿模板来说,当模板在个编译单元里被实例化时,它并不知道自己是否在别的编译单元也被实例化了。所以当--个模板在多个编译单元同时实例化成相同的类型的时候,必然会生成重复的代码。当然,最简单的方案就是不管这些,将这些重复的代码都保留下来。不过这样做的主要问题有以下几方面。
- 空间浪费。可以想象-一个有几百个编译单元的工程同时实例化了许多个模板,最后链接的时候必须将这些重复的代码消除掉,否则最终程序的大小肯定会膨胀得很厉害。
- 地址较易出错。有可能两个指向同一个的函数的指针会不相等。
- 指令运行效率较低。因为现代的CPU都会对指令和数据进行缓存,如果同样一份 指令有多份副本,那么指令Cache的命中率就会降低。
怎么解决?
一个简单有效的办法就是将每个模版的实例代码都放在一个段里,每个段只包含一个模版实例。这样链接器在最终链接的时候就可以区分这些相同的模版实例段,然后将他们合并到最后的代码段。这种做法被主流的编译器采用。实例化一个带有虚函数的类,就会有自己的虚函数表,也会有重复。对于外部内联函数和虚函数表,拷贝构造,拷贝赋值函数等代码重复的问题的解决方法与之类似。
函数级别链接
由于现在的程序和库通常来讲都非常庞大,一个目标文件可能包含成千上百个函数或变量。当我们要用到某个目标文件中的任意一个函数或变量时,就须要把它整个地链接进来,也就是说那些没有用到的函数也被-起链接了进来。这样的后果是链接输出文件会变得很大,所有用到的没用到的变量和函数都一起塞 到了输出文件中。VISUAL C+ +编译器提供了一个编译选项叫函数级别链接( Functional-Level Linking,/Gy),这个选项的作用就是让所有的函数都像前面模板函数-样,单独保存到一个段里面。当链接器须要用到某个函数时,它就将它合并到输出文件中,对于那些没有用的函数则将它们抛弃。这种做法可以很大程度上减小输出文件的长度,减少空间浪费。但是这个优化选项会减慢编译和链接过程,因为链接器须要计算各个函数之间的依赖关系,并且所有函数都保持到独立的段中,目标函数的段的数量大大增加,重定位过程也会因为段的数目的增加而变得复杂,目标文件随着段数目的增加也会变得相对较大。
全局构造和析构:
C++的全局对象是在main函数之前构造的,是在main函数之后析构的。所以ELF提供了.init .fini 两个段,保存了可执行代码。.init 在main之前执行,.fini 在main之后执行。全局构造和析构就是这么实现的。
标签:函数,--,代码,C++,编译,实例,链接,模板 From: https://www.cnblogs.com/wuyun--wy/p/16988623.html