我创建了一个简单的静态库,包含在.a
文件中。我可能会在各种项目中使用它,其中一些项目根本不需要90%的项目。例如,如果我想在AVR微型计算机上使用神经网络(我的库的一部分),我可能不需要其他一些东西,但是在我的代码中是否会链接,可能产生一个相当大的文件? / p>
我打算编译这样的程序:
g++ myProg.cpp myLib.a -o prog
答案 0 :(得分:4)
G ++只会从库中提取所需的目标文件,但这意味着如果使用单个目标文件中的一个符号,则该目标文件中的所有内容都会添加到您的可执行文件中。
一个源文件成为一个目标文件,因此只有在确定需要它们的情况下才将逻辑分组在一起才有意义。
这种做法因编译器(实际上是链接器)而异。例如,Microsoft链接器将选择对象文件,仅包含实际需要的那些部分。
答案 1 :(得分:2)
你也可以尝试将你的图书馆分成独立的小部分,只链接你真正需要的部分。
答案 2 :(得分:1)
当您链接到静态库时,链接器会提取解析代码其他部分中使用的名称的内容。通常,如果未使用该名称,则不会链接到该名称。
答案 3 :(得分:1)
GNU链接器将根据目标文件从您在目标文件上指定的库中提取所需的内容。就GNU链接器而言,目标文件是原子单元。它不会将它们分开。如果该对象文件定义了一个或多个未解析的外部引用,则链接器将引入目标文件。该目标文件可能具有外部引用。链接器将尝试解析这些,但如果不能解析,链接器会将这些添加到需要解析的引用集中。
有一些问题可以使得比需要的可执行文件大得多。大于需要,我指的是一个可执行文件,它包含永远不会被调用的函数,在程序执行期间永远不会被检查或修改的全局对象。您将拥有无法访问的二进制代码。
当目标文件包含大量函数或全局对象时,会产生其中一个问题。您的程序可能只需要其中一个,但您的可执行文件会获取所有这些,因为目标文件是链接器的原子单元。这些额外的功能将无法访问,因为从main
到这些功能没有呼叫路径,但它们仍然在您的可执行文件中。确保不会发生这种情况的唯一方法是使用“每个源文件一个函数”规则。我自己并不遵循这条规则,但我确实理解它的逻辑。
使用多态类时会出现另一组陷阱。构造函数包含自动生成的代码以及构造函数本身。该自动生成的代码调用父类的构造函数,在对象中插入指向类的vtable的指针,并根据初始化列表初始化数据成员。这些父类构造函数,vtable和处理初始化列表的机制可能是链接器需要解析的外部引用。如果父类构造函数位于更大的头文件中,那么您只需将所有内容拖到可执行文件中即可。
vtable怎么样? GNU编译器选择一个关键成员函数作为存储vtable的地方。该关键函数是类中第一个没有内联定义的成员函数。即使您没有调用该成员函数,您也可以在可执行文件中获得包含它的目标文件 - 并且您将获得该目标文件拖入的所有内容。
再次将源文件缩小到一个小尺寸有助于“看看猫拖进来的东西!”问题。特别注意包含该关键成员函数的文件是个好主意。保持该源文件较小,至少在cat将拖入的内容方面。我倾向于在该源文件中放置小的,自包含的成员函数。那些不可避免地拖入其他东西的功能不应该去那里。
vtable的另一个问题是它包含指向类的所有虚函数的指针。那些指针需要指向真实的东西。您的可执行文件将包含定义为类定义的每个虚拟函数的目标文件,包括您从未调用的函数。而且你将获得那些虚拟函数所拖入的所有东西。
这个问题的一个解决方案是避免制作大型课程。他们倾向于拖入一切。在这方面,上帝课程尤其成问题。另一个解决方案是努力思考一个函数是否真的需要是虚拟的。不要只是将函数设置为虚拟,因为你认为有一天会有人需要重载它。这是推测性的普遍性,并且对于虚拟功能,投机性的普遍性带来了高成本。