相同的 C++ 代码,不同的 link 选项会导致不同的行为?

Same C++ code, different link option leads to different behavior?

我有 2 个 cpp 文件,每个文件都声明了 1 个 class+1 个函数+1 个静态对象:

$ cat mya.cpp
#include<stdio.h>
struct A{
  A(){printf("%s\n",__FUNCTION__);}
};
void fa(){printf("%s\n",__FUNCTION__);}
static A s_obj;

$ cat myb.cpp
#include<stdio.h>
struct B{
  B(){printf("%s\n",__FUNCTION__);}
};
void fb(){printf("%s\n",__FUNCTION__);}
static B s_obj;

然后主函数调用 "fb",但不调用 "fa"。

$ cat nm.cpp
void fb();
int main()
{
    fb();
    return 0;
}

我尝试以不同的方式编译和link这些文件:

g++ -c mya.cpp -fPIC
g++ -c myb.cpp -fPIC
ar -rvs libmya.a mya.o
ar -rvs libmyb.a myb.o
ar -rvs libmystatic.a mya.o myb.o
g++ --shared -o libmyshare.so mya.o myb.o
g++ --shared -o libadyn.so mya.o
g++ --shared -o libbdyn.so myb.o
g++ nm.cpp -o use1StaticLib  -lmystatic -L. 
g++ nm.cpp -o use2StaticLib  -lmyb -lmya -L. 
g++ nm.cpp -o use1DynamicLib -lmyshare -L. 
g++ nm.cpp -o use2DynamicLib -ladyn -lbdyn -L. 
g++ nm.cpp -o useDirect mya.cpp myb.cpp

然后我发现 5 个可执行文件有不同的行为:

$ ./useDirect 
A
B
fb

$ ./use1DynamicLib 
A
B
fb

$ ./use2DynamicLib 
B
fb

$ ./use1StaticLib 
A
B
fb

$ ./use2StaticLib 
B
fb

相同的代码,不同的行为,如何才能不混淆?

我好像找到了一些线索,只要把mya.cpp和myb.cpp打包成不同的.a/.so文件,那么"A s_obj"就不会构造了。为什么? A 的构造函数有副作用,我没有指定任何 -O 优化。

如果是因为"A s_obj"是一个没有被使用的对象,所以没有linked,那么,"B s_obj"也没有被main函数使用,为什么总是构造呢?

需要听听各位专家的讲解!

我觉得这里有2个效果需要区分

首先,当您调用外部库时,动态链接器是惰性的,只会加载实际调用的库。但它会加载整个库。使用 use2StaticLib,您只使用 lib b,因此它只会加载这个。 use2DynamicLib

也是如此

二、写这个:

g++ --shared -o libmyshare.so mya.o myb.o

等同于使用

g++ --shared -o libmyshare.so myfile.o

其中 myfile.cpp 是 mya.cppmyb.cpp 的串联 您只是将两个目标文件复制到一个更大的文件中 libmyshare.so 所以这就是为什么当您调用一个myb.cpp 文件的功能,应用程序加载整个 libmyshare.so 库。然后在这种情况下调用 A 和 B 的构造函数来初始化静态对象。这不会改变 libmyshare.so 的编译是静态的还是动态的。

在任何这些场景中,libA 和 libB 的代码都包含在您的应用程序代码中(即使是静态编译时),并且所有库都通过动态链接调用。

希望对您有所帮助!