在类声明/定义中包含头文件

Including headers inside class declaration / definition

本文关键字:包含头 文件 定义 声明      更新时间:2023-10-16

我知道你可以这样做:

def.h:

A();
int x;

A.h

class A
{
public:
#include "def.h"
}

A.cpp

A::A()
{
    x = 0;
}
int main()
{
    A a;
    return 0;
}

我的问题是:你为什么要这样做?有什么好处吗?我可以看到,如果你有一些类有相同的成员,但不是相同的基础,这将是有帮助的,但它值得的麻烦吗?这不是很好读,是吗?此外,编译器如何处理这些包含?它只是粘贴标题的内容,其中包括(有点像一个宏)?

我从来没有在一个类中看到过这种情况,如果你想在另一天仍然理解代码,我建议你永远不要这样做。

也就是说,有一种情况,我发现这种技术是可以接受的,那就是当您有一个大表,您需要从中生成多个结构,如枚举和属性表。让我们有两个文件,如:

foobars.h:

enum Foobars {
#define FOOBAR(id, description, args) FOOBAR_##id,
#include "foobars.tab"
#undef FOOBAR
};
extern const char *foobar_names[];
const char *parse_foobar(Foobars fb, const char *input);

foobars.cpp:

#include "foobars.h"
const char *foobar_names[] = {
#define FOOBAR(id, description, args) description,
#include "foobars.tab"
#undef FOOBAR
};
const char *parse_foobar(Foobars fb, const char *input) {
    switch(fb) {
#define INT get_int(&input)
#define FLOAT get_float(&input)
#define STRING get_string(&input)
#define FOOBAR(id, description, args) args
#include "foobars.tab"
#undef FOOBAR
    }
return input;

神奇之处在于"foobars"。(它很特殊,所以我建议不要将其命名为anything.h或anything.hpp或任何其他常见后缀):

/* CAUTION! This file is included using C preprocessor in the middle of various structures
 * It must not contain anything except definitions of foobars in the FOOBAR macro and
 * comments. Don't forget NOT to write semicolons; some of the structures are
 * comma-separated and some semicolon-separated. FOOBAR will be defined appropriately before
 * including this file. */
FOOBAR(NULL, "Empty command, does nothing", {}) // NO semicolon!
// Also some preprocessors don't like empty arguments, so that's why {}.
// (void)0 is an alternative.
FOOBAR(FOO, "Foo bars and bazes", a = INT; b = STRING)
FOOBAR(BAR, "Bars, but does not baz", x = FLOAT)
...

另一个选择是定义一个宏来包含特殊的内容。如果表很短,宏更容易读,但如果文件很长,特殊文件更有意义。

最后一个选项是使用完全不同格式的表并生成代码,但这需要编写一些特殊的脚本来构建它,而这没有。

预处理器(在任何之前运行),当它偶然发现include时,几乎是从字面上复制该头指令的内容并将其粘贴到#include指令的位置。

像你描述的那样使用它的好处很少,主要的是你不必重复代码。

然而,在9999/10000的情况下,这绝对不值得这么麻烦。如果你在头文件的某个地方有一个错别字,你会在每个使用它的文件中得到奇怪的错误,并且在你真正打开文件并读取它之前根本不清楚它在做什么。

尽量避免。我想不出有什么情况是绝对必要的;在大多数情况下,通过继承或组合可以实现相同的效果,而不会产生任何副作用。

在像Ruby这样的语言中,这个概念被称为Mixin。因为c++中有多重继承,所以这里不需要。

我发现的一种用法是,如果您想在类定义中自动生成大量行,那么像这样包含自动生成的文件可能会很有帮助。

这些答案都很老了,但我找到了一个上面没有列出的原因。我正在编写需要访问私有成员的自动化测试,因此在许多类中使用友谊声明。由于友谊不是继承的,因此我必须在与之交互的每个类中显式声明任何新测试类的友谊。

如果有一个或多个文件按照"test_friends.h"列出我的测试类,这就容易多了:

friend class testOneFeature;
friend class testAnotherFeature;

等,在测试的类中,我可以简单地将该文件包含在声明中。

class MyClass
{
#include "test_friends.h"
public:
 //etc 
};

只是无意中发现了这一点,这对gtest fixture非常有用,因为您需要将每个测试fixture声明为FRIEND_TEST(),以便能够访问私有/受保护的成员。