【问题标题】:Is it advisable to use extern to avoid header dependency?是否建议使用 extern 来避免标题依赖?
【发布时间】:2011-08-11 04:59:53
【问题描述】:

我使用 extern 消除了翻译单元中的两个标题包含。这是可取的吗?

我的具体情况:我有一个名为ParseTree的类,它积累了Token*的。 ParseTree*Parser 的私有成员。

最初,我在parse_tree.cc 中有以下几行。

#include "parser.h"
#include "token.h"

分析我的代码后,我隔离了两个实际上具有外部依赖关系的函数,并将包含替换为以下内容:

extern std::ostream& operator<<(std::ostream& out, const Token& t); // @token.h
extern bool hasPriority(const Token* p_tok1, Token* p_tok2); // @parser.h

这两种解决方案似乎都有效。选择 extern 而不是 include 时,我应该注意哪些隐患?

【问题讨论】:

  • 外部链接是函数声明的默认设置,因此extern 本身是多余的。
  • 这是我忽略的一个很好的观点。

标签: c++ coding-style include extern


【解决方案1】:

如果你使用extern 声明,你会在你使用它的任何地方重新声明函数的原型,从而不必要地重复自己。有了头文件,如果要更改原型,只需要更改一处即可。

所以不,当你已经有一个合适的头文件时不要使用extern

【讨论】:

  • 人们似乎强烈建议减少依赖关系并将翻译单元之间的关系保持在最低限度。例如,pimpl 和前向声明。这种技术与这两种技术有何不同?尤其是 Pimpl 成语有很多潜在的缺点/并发症..
  • +1 表示“合适的头文件”。当许多大型头文件被单个包含链接到时,我已经看到使用其他选项作为构建时优化。
  • @Jordan:当您更新真正的(一个)定义时,您不必更改 pimpls。您所描述的 is 前向声明,理想情况下它将包含在“合适的”(不是依赖膨胀的)头文件中。
  • @Jordan:使用 Pimpl,您不会更改 pimpl 指针的类型。使用前向声明,您不太可能更改类型的名称。但是使用函数,您可以轻松地将参数的定义更改为该函数。这只是变化频率的问题。此外,人们通常建议在 header 中提出声明,该声明与这些类型的定义是分开的。这样,如果您需要声明,您可以包含一个小标题。
  • @Jordan,如果您认为值得,您可以创建新的标头,这些标头将包含在 parse_tree.cc、parser.h 和 token.h 中;但是有两条规则我强烈建议不要打破:一个标题应该是自包含的(理想情况下通过拥有一个具有第一个标题的 CU 来强制执行)并且,除了类,最多应该有一个声明不是定义,需要它的CU应该包括包含它的文件。尽量减少包含的数量是值得的,但要服从这些规则。
【解决方案2】:

这两种解决方案似乎都有效。选择 extern 而不是 include 时,我应该注意哪些隐患?

是的。 API 可能会发生变化,编译器将无法为您工作,它会对您不利。

学习使用编译器对您有利。不,从长远来看,不仅仅是“现在”。从长远来看,你会犯愚蠢的错误,而不是当你头脑中的一切都是新鲜的。是的,你大脑的记忆在某种程度上是不稳定的。

【讨论】:

    【解决方案3】:

    通常通过前向声明类来减少依赖关系。这是一个例子。假设我们有两个必备的头文件 parser.h 和 token.h,定义如下:

    parser.h:

    class Parser
    {
    public:
      void doFoo();
      void doBar();
      // ... lots of other stuff
    };
    

    token.h:

    class Token
    {
    public:
      void doQuux();
      void doBaz();
      // ... continues for a while
    };
    

    现在,有一个使用这两个的“用户,h”,而不是像这样写:

    #include "parser.h"
    #include "token.h"
    
    void useParserAndToken( Parser &, Token & );
    

    你写

    class Parser;
    class Token;
    
    void useParserAndToken( Parser &, Token & );
    

    这样你最终会说有 Parser 和 Token 类,但它们是如何定义的并不重要。这样可以加快编译速度。

    为了减少依赖,你通常只前向声明类。另一方面,重复函数声明没有多大意义。

    【讨论】:

      猜你喜欢
      • 2016-03-18
      • 2017-09-27
      • 1970-01-01
      • 2013-02-06
      • 1970-01-01
      • 2015-10-07
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多