使用#include 把大块重复的代码放到一个单独的文件里,这样and/or正常吗?
Is it okay and/or normal to use #include to put large chunks of repetitive code into a separate file?
我一直在剖析一些代码,我看到了一些我以前没有见过的东西,我想知道这是否是一种 good/bad 做法,是否正常。
基本上有一个头文件,其中有一个 class 定义 class 和一堆(大约 90 个)纯虚函数。这些虚函数有很多,所以它们都放在一个单独的文件中,然后包含在 class 定义中,如下所示:
Foo.h
class Foo
{
public:
virtual ~Foo() {};
#define FOO_VIRTUAL_IMPL = 0
#include "Foo_prototypes.h"
};
Foo_prototypes.h
#if ! defined(FOO_VIRTUAL_IMPL)
# define FOO_VIRTUAL_IMPL
#endif
virtual void doSomething() FOO_VIRTUAL_IMPL;
virtual void doSomethingElse() FOO_VIRTUAL_IMPL;
还有
像这样使用定义宏也很常见(即允许相同的
包含用于纯虚函数和普通虚函数的文件)?这种东西是经常使用,还是只是为了节省一点 time/effort 的小技巧?
我想这些东西让代码对我来说似乎不太可读,但这可能只是因为我不习惯这些技巧,一旦我习惯了它们,我就会更好地阅读这类代码。
有问题的代码是 Interactive Brokers 的 C++ API,如果有人想在上下文中查看它的话。相关文件是:EWrapper.h 和 TestCppClient.h 和 EWrapper_prototypes.h.
好吧,这不是我所谓的 well-styled 代码。是的,从技术上讲,您可以在那种地方包含 headers,但它并不完全标准,所以我不推荐它。总的来说,"stay away from the dusty corners of a language"。但是,如果你真的想做那种事,你可以。但是,不遵循标准做法会产生两种影响。
Non-standard 代码通常更难阅读,因此很少有人能够或愿意做出贡献。
Non-standard 代码可能有更多未被注意到的错误,只是因为它不标准。
我之前提到的错误示例是 FOO_VIRTUAL_IMPL
的工作方式。 #define
不限于范围,因此这对您的所有代码都是可见的。在一个 header 中 #define
它真的很容易,而在另一个 header 中根本不定义它。这会导致第二个 header 中的所有虚函数都是纯虚函数,可能不是您想要的。
编辑:另外,正如 Caleth 所说,如果您的 class 需要那么多重复代码,最好重新设计您的 class。
更安全的实施方式是:
#define FOO_PROTOTYPES(FOO_VIRTUAL_IMPL)\
virtual void doSomething() FOO_VIRTUAL_IMPL;\
virtual void doSomethingElse() FOO_VIRTUAL_IMPL;
class Foo
{
public:
virtual ~Foo() {};
FOO_PROTOTYPES( = 0 );
};
class FooImpl : public Foo
{
public:
virtual ~FooImpl() {};
FOO_PROTOTYPES( override );
};
一切都在一个 header 中,避免在一个 header 中意外使用另一个 header 中定义的 FOO_VIRTUAL_IMPL
值。
但是,如果您的 class 有足够的方法来使此类构造物有所值,那么可能是时候将您的 class 重构为更小的 classes。
这里有几个问题。
如果您有一个 class 一遍又一遍地使用相同的纯虚函数,那么它就是一个定义接口。绝对没有理由不把事情包装在一个定义明确、命名的接口中。现代 IDE 将在实现这些接口时为您提供支持(因此不需要宏魔法)。
一般来说,使用宏是不好的。我们不会在任何代码审查中接受宏编程。查看 C++ ISO 指南以获取更多信息:
Macros are a major source of bugs. Macros don't obey the usual scope and type rules. Macros ensure that the human reader sees something different from what the compiler sees. Macros complicate tool building.
https://github.com/isocpp/CppCoreGuidelines/blob/master/CppCoreGuidelines.md#Res-macros
我会非常警惕未出现在::
范围内的#include
,尤其是 因为它是一个以 .h
.
结尾的文件
我也对 #define
持谨慎态度,尤其是当您在 #include
之后 #undef
它时不这样做。
如果您发现自己正在编写 多个 classes 在一个抽象基础上实现数十个虚拟方法,我会首先重新考虑 class 设计。您或许可以将您的大界面拆分为一组相关界面。
我一直在剖析一些代码,我看到了一些我以前没有见过的东西,我想知道这是否是一种 good/bad 做法,是否正常。
基本上有一个头文件,其中有一个 class 定义 class 和一堆(大约 90 个)纯虚函数。这些虚函数有很多,所以它们都放在一个单独的文件中,然后包含在 class 定义中,如下所示:
Foo.h
class Foo
{
public:
virtual ~Foo() {};
#define FOO_VIRTUAL_IMPL = 0
#include "Foo_prototypes.h"
};
Foo_prototypes.h
#if ! defined(FOO_VIRTUAL_IMPL)
# define FOO_VIRTUAL_IMPL
#endif
virtual void doSomething() FOO_VIRTUAL_IMPL;
virtual void doSomethingElse() FOO_VIRTUAL_IMPL;
还有
像这样使用定义宏也很常见(即允许相同的 包含用于纯虚函数和普通虚函数的文件)?这种东西是经常使用,还是只是为了节省一点 time/effort 的小技巧?
我想这些东西让代码对我来说似乎不太可读,但这可能只是因为我不习惯这些技巧,一旦我习惯了它们,我就会更好地阅读这类代码。
有问题的代码是 Interactive Brokers 的 C++ API,如果有人想在上下文中查看它的话。相关文件是:EWrapper.h 和 TestCppClient.h 和 EWrapper_prototypes.h.
好吧,这不是我所谓的 well-styled 代码。是的,从技术上讲,您可以在那种地方包含 headers,但它并不完全标准,所以我不推荐它。总的来说,"stay away from the dusty corners of a language"。但是,如果你真的想做那种事,你可以。但是,不遵循标准做法会产生两种影响。
Non-standard 代码通常更难阅读,因此很少有人能够或愿意做出贡献。
Non-standard 代码可能有更多未被注意到的错误,只是因为它不标准。
我之前提到的错误示例是 FOO_VIRTUAL_IMPL
的工作方式。 #define
不限于范围,因此这对您的所有代码都是可见的。在一个 header 中 #define
它真的很容易,而在另一个 header 中根本不定义它。这会导致第二个 header 中的所有虚函数都是纯虚函数,可能不是您想要的。
编辑:另外,正如 Caleth 所说,如果您的 class 需要那么多重复代码,最好重新设计您的 class。
更安全的实施方式是:
#define FOO_PROTOTYPES(FOO_VIRTUAL_IMPL)\
virtual void doSomething() FOO_VIRTUAL_IMPL;\
virtual void doSomethingElse() FOO_VIRTUAL_IMPL;
class Foo
{
public:
virtual ~Foo() {};
FOO_PROTOTYPES( = 0 );
};
class FooImpl : public Foo
{
public:
virtual ~FooImpl() {};
FOO_PROTOTYPES( override );
};
一切都在一个 header 中,避免在一个 header 中意外使用另一个 header 中定义的 FOO_VIRTUAL_IMPL
值。
但是,如果您的 class 有足够的方法来使此类构造物有所值,那么可能是时候将您的 class 重构为更小的 classes。
这里有几个问题。
如果您有一个 class 一遍又一遍地使用相同的纯虚函数,那么它就是一个定义接口。绝对没有理由不把事情包装在一个定义明确、命名的接口中。现代 IDE 将在实现这些接口时为您提供支持(因此不需要宏魔法)。
一般来说,使用宏是不好的。我们不会在任何代码审查中接受宏编程。查看 C++ ISO 指南以获取更多信息:
Macros are a major source of bugs. Macros don't obey the usual scope and type rules. Macros ensure that the human reader sees something different from what the compiler sees. Macros complicate tool building.
https://github.com/isocpp/CppCoreGuidelines/blob/master/CppCoreGuidelines.md#Res-macros
我会非常警惕未出现在::
范围内的#include
,尤其是 因为它是一个以 .h
.
我也对 #define
持谨慎态度,尤其是当您在 #include
之后 #undef
它时不这样做。
如果您发现自己正在编写 多个 classes 在一个抽象基础上实现数十个虚拟方法,我会首先重新考虑 class 设计。您或许可以将您的大界面拆分为一组相关界面。