C++编写纯头文件代码的技巧

为什么要编写纯头文件代码呢?传统.cpp代码要求使用者要么使用底层运行库兼容的预编译版本,要么重新编译,操作起来非常麻烦,而纯头文件代码则简单得多,直接#include就可以了。

如果你认真研究过C++的模板,你会发现它们的函数体是推荐定义在头文件中的,这样每当用到时便可以实例化它们。链接时则会遇到一个问题,实例化的代码可能在不同.obj/.o文件中产生重复定义,而传统上C++符号是不允许重复定义的,怎么办呢?实际上,编译器会把它们标记为可合并符号,链接时会随机选择一个链接,所以并不会产生重复定义问题。

inline这个关键字也可以产生同样的效果。inline在C++中表示推荐内联,因此应该被定义在头文件中,并且只要不标记为static,它就是extern的,标准规定了它们在所有.obj/.o文件中具有相同的地址,也就是可以产生可合并符号。

在C++17之前全局变量和类成员静态变量(其实是类命名空间内的全局变量)并不能声明为inline。编译器厂商早就想到了这个问题,并提供了不同的编译器扩展。在Windows平台,可以使用__declspec(selectany)声明可合并符号,但是仅适用于全局变量,不适用于全局函数。在Linux平台,可以使用__attribute__((weak))。

另外要注意的是C语言不提供类似的机制,C语言的inline并不会产生可合并符号。

有了这些机制的支持,我们就能在头文件中生成可合并符号,进而编写纯头文件代码了。

#if __cpp_inline_variables >= 201606L
#define INLINEGLOBAL inline // C++17
#elif defined(_MSC_VER)
#define INLINEGLOBAL __declspec(selectany) // Visual C++
#else
#define INLINEGLOBAL __attribute__((weak)) // GCC/Clang
#endif

namespace MyLib {

INLINEGLOBAL int global_num = 1;

inline int func(int a, int b)
{
    static int static_num = 0;
    return a + b + static_num++;
}

class MyClass {
public:
    static int static_num;
    MyClass();
};

INLINEGLOBAL int MyClass::static_num = 0;

inline MyClass::MyClass()
{
    static_num++;
}

} // namespace MyLib
发布了29 篇原创文章 · 获赞 1 · 访问量 3420

猜你喜欢

转载自blog.csdn.net/defrag257/article/details/82053682