避免重新定义单个标头的变量

时间:2016-09-12 12:54:37

标签: c++ c++11 namespaces extern

我对代码有一个标头要求,这意味着不应该将声明和定义拆分成单独的标头和源文件。我已经正确地实现了它,它可以用于我的用例,这个头文件只包含在单个源文件中。

现在,当涉及到它在多个源文件中使用时(多个.cpp包含它),它将失败,链接器错误沿着一些变量被重新声明的行。那是因为我的代码就像 -

#ifndef HEADER_HPP
#define HEADER_HPP

....

std::streambuf const *R_coutbuf = std::cout.rdbuf();
std::streambuf const *R_cerrbuf = std::cerr.rdbuf();
std::streambuf const *R_clogbuf = std::clog.rdbuf();

void doSomething(){
    [uses if(R_coutbuf) and others]
}

....

#endif HEADER_HPP

现在最好的解决方案是在头文件中声明这些变量并在单个cpp文件中定义/分配它们,但正如我所说,我希望能够使用单个头文件执行此操作。这就带来了一个问题,如果有多个源文件将包含它,将会有重新声明。

到目前为止,我不确定我该怎么做才能做到这一点,但我有两个想法 -

#ifdef DEFINE_VARIABLES
#define EXTERN /* nothing */
#else
#define EXTERN extern int
#endif /* DEFINE_VARIABLES */

EXTERN global_variable = something;

我对它不太确定,这甚至会起作用吗?

我想到的第二种方法是将它放在匿名命名空间中,我正在尝试这个并且到目前为止它的构建成功 -

#ifndef HEADER_HPP
#define HEADER_HPP

....

namespace R {

    namespace {
        std::streambuf const *R_coutbuf = std::cout.rdbuf();
        std::streambuf const *R_cerrbuf = std::cerr.rdbuf();
        std::streambuf const *R_clogbuf = std::clog.rdbuf();
    }
    void doSomething(){
        [uses if(R_coutbuf) and others]
    }
}

....

#endif HEADER_HPP

还有其他方法可以达到这个目的吗?我上述任何一种方式都有问题。

2 个答案:

答案 0 :(得分:5)

您可以在函数中使变量成为局部静态变量:

inline std::streambuf const*& R_coutbuf() {
    static std::streambuf const* b = std::cout.rdbuf();
    return b;
}

答案 1 :(得分:1)

  

上述任何一种方式都有问题。

宏技巧

  • 正反
    • 在目标文件中生成最少数量的变量。请参阅匿名命名空间的con。
  • 缺点
    • 非常脆弱。当包含头时,程序员必须知道其他任何的其他现有源是否包含变量定义。当源文件数量增加时,这不能很好地扩展

内部链接(匿名命名空间)

  • 正反
    • 没有宏技巧的缺点。
  • 缺点
    • 包含标题的每个目标文件都有自己的变量副本。我不确定,但变量是const,并且所有变量在运行时都具有相同的值,链接器可以能够丢弃重复项。在任何情况下,差异只是您的程序的几个字节。修改单个变量而不是修改共享变量会改变程序的含义。

为了进行比较,让我们考虑您已经排除的单独的源文件选项。

  • 正反
    • 在目标文件中生成最少数量的变量,例如宏技巧。
    • 没有宏技巧的缺点。
  • 缺点
    • 无(除非您考虑单独使用单独的源文件)。
  

还有其他方法可以实现吗?

不是真的。您可以使用static关键字而不是匿名命名空间来声明内部链接。

PS。如果在标头中定义任何非模板函数,则必须将它们声明为内联。您的匿名命名空间示例无法执行此操作。