【发布时间】:2015-01-19 01:52:53
【问题描述】:
^ 不是重复的,在这里我正在调查为什么首选一种方法,而不是有什么区别,仔细阅读会使同行评审员感到震惊,这不是重复的。
我正在阅读:
我的脑海里突然冒出这个问题。是否有任何合理的理由使静态或共享库优先于普通的 Obj 文件?
在创建静态/共享库时,我们会丢失很多信息以及优化的机会(这对我来说并不重要)。相反,我们可以将源文件编译成 Obj 文件,然后将所有 Obj 文件(也是库的 Obj 文件)链接到最终的可执行文件中。
=>
我们保留所有信息,对于异常处理和防止重复的 type_info 尤其有用(C++ 的依赖注入依赖于 C++11 Type_info,但不能保证您不会为不同库中的不同类获得重复的 std::type_info 对象)。
如果可见性让您担心,您可以执行“Ghost 编译/链接”步骤(编译应用程序,然后与静态库链接以查看我们是否因为访问内部内容而获得“未定义符号”),然后继续进行真正的编译/Link(一切都到 Obj 文件,然后 Obj 到可执行文件)
最终的可执行文件将更小更快,不会出现异常问题,并且构建系统将更加 Emscripten 友好:)。
您是否发现可能存在的问题(我已经在使用它并且工作正常,但可能已经存在的代码可能存在“重复”问题?对于大型代码库可能不可行?)
小例子:
我的静态库是从 2 个文件编译而来的:
MyFoo.hpp
//declare MyFoo to be internal to my library
void __attribute__ ((visibility ("hidden"))) MyFoo();
MyFoo.cpp
#include <MyFoo.hpp>
#include <iostream>
void MyFoo(){
std::cout<<"MyFoo()"<<std::endl;
}
MyBar.cpp
#include <MyBar.hpp>
#include <MyFoo.hpp>
#include <iostream>
void MyBar(){
std::cout<<"MyBar() calling "; MyFoo(); //calling MyFoo
}
当我们编译时,我们得到 2 个 ObjFiles
MyFoo.o
MyBar.o
当我们链接时我们得到
MyLib.a
MyBar,仍然可以“看到”并调用 MyFoo(否则无法编译)。
当我创建可执行文件时,如果我链接到 MyLib.a,我只能调用 MyBar,这是正确的
#include <MyBar.hpp>
#include <MyFoo.hpp>
int main(){
MyBar(); //ok
//MyFoo(); // error undefined symbol
return 0;
}
那是因为我丢失了一些信息(在大多数情况下需要:注意我必须指定 hidden),但是因此“可见性”功能变成了一个问题,因为隐藏东西我们最终可能会使用 不同的类(在不同的库中)具有相同的完整限定名:
这是抛出异常或尝试使用std::type_info时的问题
所以唯一可行的解决方案似乎是进行 2 步编译,1 只是为了检查我们没有破坏可见性(因此 API 合同),第二次构建是为了避免上述链接中的问题(调试异常很奇怪行为或神秘的崩溃)。
将MyFoo.o,MyBar.o,main.o 链接在一起在概念上是错误的,因为允许编译以下代码
#include <MyBar.hpp>
#include <MyFoo.hpp>
int main(){
MyBar(); //ok
MyFoo(); // compile NOT OK!
return 0;
}
但是将目标文件链接在一起是避免异常问题的唯一方法。
【问题讨论】:
-
建库时会丢失哪些信息?它们只是一堆与
ar连接的目标文件。 -
当你玩的时候没有班级的可见性。您可以
#include <foo>并在创建库时使用 foo(因此 foo 在对象文件级别可见),但是当您创建可执行文件并链接到静态库时,您会得到 foo 的未定义符号(您丢失了有关 foo 的信息)。所以实际上隐藏信息通常是好的,如果你把它隐藏给最终用户是好的,但可能会成为异常处理和 dynamic_cast 的问题。 -
@DarioOO 我还是不明白,你能举个小例子说明你的意思吗?
-
静态库本质上是目标文件的 ZIP 文件(尽管使用不同的格式,实际上不是 ZIP)。
-
@DarioOO:需要明确的是,
#include包括源文件,而不是目标文件。