7

C++ では、プログラマーが関数ブロック内の任意の場所で変数を宣言できるようにすることで、読みやすさが向上したとプログラミングのクラスで聞いたことがある。このようにして、変数はそれを処理するコードのセクションと一緒にグループ化されました。

インクルードについても同じことをしてみませんか? 別の言い方をすれば、インクルード ファイルを実際に使用する定義の隣に置くことが推奨されないのはなぜですか?

parser::parser()
{
  // some initialization goes there which does not make use of regex
}

#include <boost/regex.hpp>
parser::start()
{
  // here we need to use boost regex to parse the document
}
4

3 に答える 3

8

これの理由の 1 つは、#includeコンテキストがなく、単なるプレーン テキストのインクルージョンであり、コードの途中にあると望ましくない影響が生じる可能性があることです。たとえば、名前空間があり、このファイル内のすべてのコードが名前空間に属しているとします。

// Include on demand:
namespace ns {
   void f() {} // does not need anything
//... lots of other lines of code
#include <vector>
   void g() { std::vector<int> v; }
}

これで問題なくコンパイルできるかもしれません...そして間違っています。インクルードは名前空間内にあるため、ファイルの内容は内部にダンプされ、インクルードされたnsファイルは宣言/定義します::ns::std::vector。ヘッダーのみであるため、正常にコンパイルされる可能性もありますが、別のサブシステム (別の名前空間) とのインターフェイスでそれを使用しようとすると失敗するだけです。これは修正できます。すべてのコンテキストを閉じて、包含して同じコンテキストを再度開く...

翻訳単位のコードが実際にインクルードに影響を与える可能性がある状況は他にもあります。たとえば、using namespace翻訳単位にディレクティブを追加したとします。その後に含まれるヘッダーには、using namespaceディレクティブが配置され、望ましくない効果が生じる可能性があります。

また、さまざまな方法でエラーが発生しやすくなる可能性があります。fforintとの異なるオーバーロードを定義した 2 つのヘッダーを考えてみましょうdouble。1 つ (たとえばintバージョン) をファイルの先頭に追加して使用し、次にもう 1 つを追加して使用することができます。f(5.0)ここで、2 番目のヘッダーが含まれる行の上で呼び出すと、intバージョンは --この時点でコンパイラが利用できるオーバーロードのみ -- と呼ばれます。これはエラーをキャッチするのが難しいでしょう。まったく同じコード行が、ファイル内の異なる場所ではまったく異なる意味を持ちます (確かにそれは既に当てはまりますが、ファイル内の宣言内では、どれがピックアップされているのか、そしてその理由を簡単に見つけることができます)。

一般に、インクルードはコンポーネントで使用する要素を宣言し、それらを一番上に置くと、一目で依存関係のリストが提供されます。

于 2012-07-27T22:17:42.933 に答える
4

次のファイルを想像してください。

#include <someheader>

namespace myns {
  void foo() {
  }

  void bar() {
    // call something from someheader:
    func();
  }
}

#include <someheader>使用箇所の近くに置きたくなるかもしれません。代わりに次のように記述すれば問題ありません。

namespace myns {
  void foo() {
  }
}

#include <someheader>

namespace myns {      
  void bar() {
    // call something from someheader:
    func();
  }
}

#ifdef問題は中/大サイズのファイルにあります。インデント スタイルによっては、名前空間 (およびその他の s) 内でどれだけ深くネストされているかを簡単に追跡できなくなります。後で戻ってきて、物事を移動したり、別のネストされた名前空間を追加したりすることを決定する場合があります。

したがって、#include常に先頭に を記述すれば、誤って次のように記述して噛まれることはありません。

namespace myns {
  void foo() {
  }

// Whoops, this shouldn't be inside myns at all!
#include <someheader>

  void bar() {
    // call something from someheader:
    func();
  }
}

正確に何が入っているかによって、間違っているか非常に間違っているかのどちらかになります<someheader>。(たとえば、複数の定義を持つ ODR に違反することにより、最終的に UB になる可能性があります。それ以外の場合は合法で同一のトークンのシーケンスが異なる機能に一致し、§ 3.2.5 に違反します)。

于 2012-07-27T22:19:04.197 に答える
0

ほとんどのプログラマーはファイルの先頭にインクルードすることに慣れているため、お勧めできません。習慣の力と、既存のコードの 99% との一貫性のためです。

個人的にどんなヘッダーが含まれているか知りたいときは、一番上だけを見ます。特殊な (そしてありがたいことにまれな) 何かが怪しい場合、ファイル全体でインクルードを探すことがあります。

コンパイラにとっては、とにかく違いはありません。

于 2012-07-27T22:11:15.643 に答える