我怀疑是否可以再次使用源文件lib1.so
和common.cpp
使用相同的源文件lib2.so
构建common.cpp
。现在,我想使用这两个库来构建应用程序APP
,
我的问题是
有可能还是会给我错误?
如果可以成功构建,那么如何解决命名问题?
F.e.假设foo
是common.cpp
中的类。 foo_v1
是lib1.so中foo的对象,而foo_v2
是lib2.so中foo的对象。现在在APP
辫子期间会发生什么?也有可能在APP
应用程序中创建foo对象吗?
最佳答案
自然会建议您考虑构建共享的通用功能
通过lib1.so
和lib2.so
进入不同的共享库libcommon.so
。
但是,如果您仍然想静态链接通用功能
完全相同1
进入lib1.so
和lib2.so
,您可以将这两个共享库与
您的程序。链接器将不会有任何问题。这是一个
插图:
普通
#ifndef COMMON_H
#define COMMON_H
#include <string>
struct common
{
void print1(std::string const & s) const;
void print2(std::string const & s) const;
static unsigned count;
};
common.cpp
#include <iostream>
#include "common.h"
unsigned common::count = 0;
void common::print1(std::string const & s) const
{
std::cout << s << ". (count = " << count++ << ")" << std::endl;
}
void common::print2(std::string const & s) const
{
std::cout << s << ". (count = " << count++ << ")" << std::endl;
}
foo.h
#ifndef FOO_H
#define FOO_H
#include "common.h"
struct foo
{
void i_am() const;
private:
common _c;
};
#endif
foo.cpp
#include "foo.h"
void foo::i_am() const
{
_c.print1(__PRETTY_FUNCTION__);
}
bar.h
#ifndef BAR_H
#define BAR_H
#include "common.h"
struct bar
{
void i_am() const;
private:
common _c;
};
#endif
bar.cpp
#include "bar.h"
void bar::i_am() const
{
_c.print2(__PRETTY_FUNCTION__);
}
现在,我们将创建两个共享库
libfoo.so
和libbar.so
。的我们需要的源文件是
foo.cpp
,bar.cpp
和common.cpp
。第一将它们全部编译为PIC (Position Independent Code
目标文件:
$ g++ -Wall -Wextra -fPIC -c foo.cpp bar.cpp common.cpp
这是我们刚刚制作的目标文件:
$ ls *.o
bar.o common.o foo.o
现在使用
libfoo.so
和foo.o
链接common.o
:$ g++ -shared -o libfoo.so foo.o common.o
然后使用
libbar.so
和(再次)bar.o
链接common.o
$ g++ -shared -o libbar.so bar.o common.o
我们可以看到
common::...
符号是由libfoo.so
定义和导出的:$ nm -DC libfoo.so | grep common
0000000000202094 B common::count
0000000000000e7e T common::print1(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&) const
0000000000000efa T common::print2(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&) const
(
T
表示在代码部分中定义,B
表示在未初始化的数据部分中定义)。关于libbar.so
完全一样$ nm -DC libbar.so | grep common
0000000000202094 B common::count
0000000000000e7e T common::print1(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&) const
0000000000000efa T common::print2(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&) const
现在,我们将创建一个与这些库链接的程序:
main.cpp
#include "foo.h"
#include "bar.h"
int main()
{
foo f;
bar b;
common c;
f.i_am();
b.i_am();
c.print1(__PRETTY_FUNCTION__);
return 0;
}
它调用
foo
;它调用bar
,并调用
common::print1
。$ g++ -Wall -Wextra -c main.cpp
$ g++ -o prog main.o -L. -lfoo -lbar -Wl,-rpath=$PWD
它的运行方式如下:
$ ./prog
void foo::i_am() const. (count = 0)
void bar::i_am() const. (count = 1)
int main(). (count = 2)
很好您可能担心静态类变量的两个副本
common::count
将最终出现在程序中-一个来自libfoo.so
,另一个来自libbar.so
,而
foo
将增加一个副本,而bar
将增加另一个副本。但是那没有发生。链接器如何解析
common::...
符号?好了,我们需要找到它们的变形形式,如链接器所见:
$ nm common.o | grep common
0000000000000140 t _GLOBAL__sub_I_common.cpp
0000000000000000 B _ZN6common5countE
0000000000000000 T _ZNK6common6print1ERKNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEE
000000000000007c T _ZNK6common6print2ERKNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEE
它们全都有了,用
c++filt
可以分辨出是哪个:$ c++filt _ZN6common5countE
common::count
$ c++filt _ZNK6common6print1ERKNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEE
common::print1(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&) const
$ c++filt _ZNK6common6print2ERKNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEE
common::print2(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&) const
现在,我们可以重新进行
prog
的链接,这一次要求链接器告诉我们名称。定义或引用了这些
common::...
符号的输入文件。这个诊断链接有点麻烦,所以我将
\
拆分:$ g++ -o prog main.o -L. -lfoo -lbar -Wl,-rpath=$PWD \
-Wl,-trace-symbol=_ZN6common5countE \
-Wl,-trace-symbol=_ZNK6common6print1ERKNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEE \
-Wl,-trace-symbol=_ZNK6common6print2ERKNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEE
main.o: reference to _ZNK6common6print1ERKNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEE
./libfoo.so: definition of _ZN6common5countE
./libfoo.so: definition of _ZNK6common6print2ERKNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEE
./libfoo.so: definition of _ZNK6common6print1ERKNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEE
./libbar.so: reference to _ZN6common5countE
./libbar.so: reference to _ZNK6common6print2ERKNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEE
./libbar.so: reference to _ZNK6common6print1ERKNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEE
因此,链接器告诉我们已将
common::count
的定义链接到./libfoo.so
。同样的common::print1
的定义。同样,common::print2
的定义。它链接了所有common::...
中的libfoo.so
符号定义。它告诉我们
common::print1
中对main.o
的引用已解析为libfoo.so
中的定义。同样地common::count
中对libbar.so
的引用。同样,对common::print1
和common::print2
中的libbar.so
。程序中的所有common::...
符号引用均已解析为libfoo.so
提供的定义。因此,没有多个定义错误,也没有关于使用
common::...
符号的“副本”或“版本”的不确定性通过程序:它仅使用
libfoo.so
中的定义。为什么?仅仅因为
libfoo.so
是链接中提供定义的第一个库用于
common::...
符号。如果我们以prog
的顺序重新链接-lfoo
,而-lbar
的顺序相反:$ g++ -o prog main.o -L. -lbar -lfoo -Wl,-rpath=$PWD \
-Wl,-trace-symbol=_ZN6common5countE \
-Wl,-trace-symbol=_ZNK6common6print1ERKNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEE \
-Wl,-trace-symbol=_ZNK6common6print2ERKNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEE
main.o: reference to _ZNK6common6print1ERKNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEE
./libbar.so: definition of _ZN6common5countE
./libbar.so: definition of _ZNK6common6print2ERKNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEE
./libbar.so: definition of _ZNK6common6print1ERKNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEE
./libfoo.so: reference to _ZN6common5countE
./libfoo.so: reference to _ZNK6common6print2ERKNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEE
./libfoo.so: reference to _ZNK6common6print1ERKNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEE
然后我们得到完全相反的答案。程序中所有
common::...
符号引用现在解析为
libbar.so
提供的定义。因为libbar.so
首先提供了它们。仍然没有不确定性,并且对程序没有影响,因为两个
libfoo.so
和
libbar.so
链接了同一对象文件common::...
中的common.o
定义。链接器不会尝试查找符号的多个定义。一旦找到
符号S的定义,在输入目标文件或共享库中,它将引用绑定到
S到找到的定义,并通过解析S完成。
不在乎它以后发现的共享库是否可以提供S的另一个定义,相同或不同,
即使该后来的共享库解析了S以外的其他符号。
导致多定义错误的唯一方法是强制链接程序
静态链接多个定义,即强制其物理合并到输出二进制文件中
两个目标文件
obj1.o
和obj2.o
都包含定义S。如果这样做,则竞争的静态定义将具有完全相同的状态,并且仅
程序可以使用一个定义,因此链接器必须使您失败。但这并不需要引起任何注意
由共享库提供的S的动态符号定义(如果它已经解析了S,但尚未这样做)。
[1]当然,如果使用不同的预处理器,编译器或链接选项编译和链接
lib1
和lib2
,则可以在某种程度上破坏“公共”功能。
关于c++ - C++-如果两个库使用相同的源代码进行构建会发生什么,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/54786262/