提问人:Luchian Grigore 提问时间:9/8/2011 更新时间:1/3/2022 访问量:3122
在类声明/定义中包含标头
Including headers inside class declaration / definition
问:
我知道你可以做这样的事情:
def.h:
A();
int x;
啊
class A
{
public:
#include "def.h"
}
.cpp
A::A()
{
x = 0;
}
int main()
{
A a;
return 0;
}
我的问题是:你为什么要这样做?有什么优势吗?我可以看到,如果您有一些成员相同但基础不相同的课程,那会有多大帮助,但这值得麻烦吗?它不是很可读,是吗? 此外,编译器如何处理这些包含?它是否只是将标题的内容粘贴到包含它的位置(有点像宏)?
答:
预处理器(在任何东西之前运行)在偶然发现 时,几乎从字面上复制了该标头的内容并将其粘贴到指令的位置。include
#include
像你描述的那样使用它的优点很少,主要的好处是你不必复制代码。
但是,在 9999/10000 的情况下,绝对不值得麻烦。如果你在头文件中的某个地方有一个错别字,你会在每个使用它的文件中得到奇怪的错误,而且在你真正打开文件并阅读它之前,根本不清楚它在做什么。
如果可能的话,请避免这种情况。我想不出绝对必要的情况;大多数情况下,通过遗传或组合可以达到相同的效果,而不会产生所有副作用。
在像 Ruby 这样的语言中,这个概念被称为 Mixin。由于我们在 C++ 中有多个继承,因此我们在这里不需要它。
评论
A a; mixin& b = a;
我从未在课堂上看到过这种情况,如果您前几天还想理解代码,建议您永远不要这样做。
也就是说,在一种情况下,我发现这种技术是可以接受的,那就是当你有一个大表时,你需要从中生成多个结构,如枚举和属性表。让我们有两个文件,例如:
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.tab”中(它很特别,所以我建议不要将其称为 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 "moc_qobject.cpp"
moc_qobject.cpp
这些答案都非常古老,但我找到了上面没有列出的理由。我正在编写自动化测试,这些测试需要访问私人成员,因此在许多课程中使用友谊声明。 由于友谊不是继承的,因此我必须在与它交互的每个类中显式声明任何新测试类的友谊。
如果有一个或多个文件按照“test_friends.h”列出我的测试类,这要容易得多:
friend class testOneFeature;
friend class testAnotherFeature;
等等,在测试的类中,我可以简单地将该文件包含在声明中。
class MyClass
{
#include "test_friends.h"
public:
//etc
};
只是偶然发现了这一点,它对 gtest 夹具非常有用,因为您需要将每个测试夹具声明为 FRIEND_TEST() 才能访问私有/受保护的成员。
上一个:一个好的设计可以完全避免铸件吗?
下一个:LL 是什么意思?
评论