我正在使用图书馆。该库有一个文件,其中包含我实际想要向其他程序公开的接口,包含在foo.h
和foo.cpp
中。它还包含一堆帮助程序类和实用程序函数,文件bar1.h
,bar2.h
,bar1.cpp
,bar2.cpp
等。
如果我编译所有这些文件并将它们粘贴在.lib中,我遇到的问题是bar
文件中的某些符号具有与其他外部库中的符号相冲突的非常常见的名称需要联系。
如果所有代码都在一个.cpp文件中,我知道如何解决这个问题:我可以使用static
或namespace { }
来阻止链接器导出内部符号。但显然,如果我想在bar
中访问它,我必须在extern
foo
中声明这些内容。
我可以在namespace baz { }
中包装所有.cpp文件。如果我仔细选择baz,那么它与其他库中使用的命名空间冲突的可能性很小,这将大大解决问题。但理想情况下,foo.h
中符号之外的 nothing 应该导出到我的.lib中。有没有这样做的技术?
答案 0 :(得分:3)
你可以实现这一点,但需要付出一些代价:
在C ++中,您可以拥有内部链接。未命名的命名空间内的任何内容都有内部链接*(参见脚注),以及静态自由函数(您应该更喜欢匿名命名空间)。
更新:这是§3.5,4的C ++ 11标准引用:
未命名的命名空间或在未命名的命名空间中直接或间接声明的命名空间 内部联系。所有其他名称空间都有外部链接。具有名称空间作用域的名称 上面给出的内部链接与封闭命名空间具有相同的链接,如果它是
的名称 - 一个变量;或
- 一个功能;或
- 命名类(第9节),或在typedef声明中定义的未命名类,其中类具有用于链接目的的typedef名称(7.1.3);或
- 命名枚举(7.2),或在typedef声明中定义的未命名枚举,其中枚举具有用于链接目的的typedef名称(7.1.3);或
- 属于具有链接的枚举的枚举器;或
- 模板。
但是,内部链接适用于翻译单元,而不适用于静态库。因此,如果您使用通常的方法将每个类放在它自己的转换单元(= cpp)中,则无法在匿名命名空间中定义它们,因为您无法将它们链接在一起以构建库。
您可以通过使整个库成为一个单独的转换单元来解决这个难题:一个头提供库的公共接口,一个源具有函数定义,以及其他任何作为头,在匿名名称空间中定义:
mylib.hpp
class MyLib {
public:
int foo();
double bar(int i);
};
<强> mylib.cpp 强>
#include "mylib.hpp"
#include "mylibimpl.h"
int MyLib::foo() {
return fooimpl();
}
double MyLib::bar(int i) {
return BarImpl(i).do();
}
<强> mylibimpl.h 强>
namespace {
inline int fooimpl() { return 42; }
class BarImpl {
double d;
public:
BarImpl(int i) : d(i*3.42) {}
double do() { return 2*d; }
};
}
您现在将拥有一个翻译单元(mylib.o
/ mylib.lib
),并且无法从外部看到所有*impl
类和函数,因为它们具有内部链接。
成本是您必须重新组织内部类的源(例如,解决循环依赖)并且库的内部代码的每次简单更改都将导致对lib中的所有内容进行一次大的重新编译,因为有只有单个巨大的翻译单位。因此,只有当库代码本身非常稳定或库不是太大时才应该这样做。 除了完全隐藏内部符号之外的好处是编译器将能够提取它想要的任何优化,因为没有实现细节隐藏在不同的转换单元中。
*的脚注:强> 正如Billy ONeal评论的那样,在C ++ 03中,匿名命名空间中的实体不必然是内部链接。但是,如果它们具有外部链接,则它们的转换单元具有唯一的名称,并且实际上无法从TU外部访问,这意味着此过程也适用于C ++ 03。