避免为单个 header 重新定义变量
avoiding redefinition of variables for single header
我对代码有一个 header 要求,这意味着不应将声明和定义拆分为单独的 header 和源文件。我已经正确地实现了它并且它按照我的用例的预期工作,其中这个 header 文件应该只包含在单个源文件中。
现在,当涉及到它在多个源文件(其中包含多个 .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
现在最好的解决方案是在 header 文件中声明这些变量,并在单个 cpp 文件中 define/assign 声明它们,但正如我所说,我希望能够用一个 header 文件。这就带来了一个问题,如果多个源文件将包含它,就会有重新声明。
到目前为止,我不确定我应该怎么做,但我有两个想法 -
#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
还有其他方法可以实现吗?我上面说的这两种方式有没有问题。
Are there any problems with either of the ways I described above.
宏技巧
- 专业人士
- 在 object 文件中生成最少数量的变量。查看匿名命名空间的缺点。
- 缺点
- 非常脆弱。当包含 header 时,程序员必须知道其他现有源的 any 是否包含变量定义。当源文件数量增加时,这不能很好地扩展
内部链接(匿名命名空间)
- 专业人士
- 没有宏技巧的缺点。
- 缺点
- 包含 header 的每个 object 文件都有自己的变量副本。我不确定,但是变量是
const
并且在 运行 时所有变量都具有相同的值,链接器 可能 能够丢弃重复项.在任何情况下,对于您的程序来说,差异只是几个字节的价值。修改单个变量而不是修改共享变量会改变程序的含义。
为了比较,让我们考虑您已排除的单独源文件选项。
- 专业人士
- 在 object 文件中生成最少数量的变量,就像宏技巧一样。
- 没有宏技巧的缺点。
- 缺点
- None(除非你认为有一个单独的源文件本身就是一个骗局)。
Is there any other way I could achieve this?
不是真的。您可以使用 static
关键字而不是匿名命名空间来声明内部链接。
PS。如果您在 header 中定义任何 non-template 函数,则必须将它们声明为内联。您的匿名命名空间示例无法做到这一点。
您可以使您的变量成为函数中的局部静态变量:
inline std::streambuf const*& R_coutbuf() {
static std::streambuf const* b = std::cout.rdbuf();
return b;
}
我对代码有一个 header 要求,这意味着不应将声明和定义拆分为单独的 header 和源文件。我已经正确地实现了它并且它按照我的用例的预期工作,其中这个 header 文件应该只包含在单个源文件中。
现在,当涉及到它在多个源文件(其中包含多个 .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
现在最好的解决方案是在 header 文件中声明这些变量,并在单个 cpp 文件中 define/assign 声明它们,但正如我所说,我希望能够用一个 header 文件。这就带来了一个问题,如果多个源文件将包含它,就会有重新声明。
到目前为止,我不确定我应该怎么做,但我有两个想法 -
#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
还有其他方法可以实现吗?我上面说的这两种方式有没有问题。
Are there any problems with either of the ways I described above.
宏技巧
- 专业人士
- 在 object 文件中生成最少数量的变量。查看匿名命名空间的缺点。
- 缺点
- 非常脆弱。当包含 header 时,程序员必须知道其他现有源的 any 是否包含变量定义。当源文件数量增加时,这不能很好地扩展
内部链接(匿名命名空间)
- 专业人士
- 没有宏技巧的缺点。
- 缺点
- 包含 header 的每个 object 文件都有自己的变量副本。我不确定,但是变量是
const
并且在 运行 时所有变量都具有相同的值,链接器 可能 能够丢弃重复项.在任何情况下,对于您的程序来说,差异只是几个字节的价值。修改单个变量而不是修改共享变量会改变程序的含义。
- 包含 header 的每个 object 文件都有自己的变量副本。我不确定,但是变量是
为了比较,让我们考虑您已排除的单独源文件选项。
- 专业人士
- 在 object 文件中生成最少数量的变量,就像宏技巧一样。
- 没有宏技巧的缺点。
- 缺点
- None(除非你认为有一个单独的源文件本身就是一个骗局)。
Is there any other way I could achieve this?
不是真的。您可以使用 static
关键字而不是匿名命名空间来声明内部链接。
PS。如果您在 header 中定义任何 non-template 函数,则必须将它们声明为内联。您的匿名命名空间示例无法做到这一点。
您可以使您的变量成为函数中的局部静态变量:
inline std::streambuf const*& R_coutbuf() {
static std::streambuf const* b = std::cout.rdbuf();
return b;
}