web-dev-qa-db-ja.com

C ++での二重インクルードガードの使用

だから私は最近、私が働く場所について議論しました。そこでは、単一のガードに対するdouble includeガードの使用に疑問を抱いていました。 double guardの意味は次のとおりです。

ヘッダーファイル、「header_a.hpp」:

#ifndef __HEADER_A_HPP__
#define __HEADER_A_HPP__
...
...
#endif

ヘッダーファイルをソースファイルまたはヘッダーファイルのどこかに含める場合:

#ifndef __HEADER_A_HPP__
#include "header_a.hpp"
#endif

現在、ヘッダーファイルでのガードの使用は、既に定義されているヘッダーファイルの複数のインクルードを防止することであることを理解しています。マクロが既に定義されている場合、ヘッダーファイル全体がコンパイラーによって「空白」と見なされ、二重の包含が防止されます。簡単です。

私が理解していない問題は、#ifndef __HEADER_A_HPP__および#endif の周辺 #include "header_a.hpp"。同僚から、これにより包含に第2層の保護が追加されると言われましたが、第1層が絶対に仕事をする(またはそれを行う)場合、その第2層がどのように役立つかを確認できません。

私が思いつくことができる唯一の利点は、リンカーがファイルを見つけるのを煩わせることを完全に止めることです。これは、コンパイル時間を改善することを意図したものですか(これは利点として言及されていませんでした)、または、ここで私が見ない仕事が他にありますか?

71
sh3rifme

次のような別のインクルードガードを追加するのは悪い習慣であると確信しています。

#ifndef __HEADER_A_HPP__
#include "header_a.hpp"
#endif

理由は次のとおりです。

  1. 二重インクルードを回避するには、ヘッダーファイル自体に通常のインクルードガードを追加するだけで十分です。それは仕事をうまくやる。別のインクルードガードは、インクルードの代わりにコードを混乱させ、読みやすさを低下させます。

  2. 不要な依存関係が追加されます。ヘッダーファイル内のインクルードガードを変更する場合は、ヘッダーが含まれるallの場所で変更する必要があります。

  3. コンパイル/リンケージプロセス全体を比較するのは間違いなく最も高価な操作ではないため、合計ビルド時間はほとんど短縮できません。

  4. あらゆる価値のあるコンパイラ 既にファイル全体のインクルードガードを最適化しています

105
Edgar Rokjān

headerファイルにインクルードガードを配置する理由は、ヘッダーのコンテンツが翻訳ユニットに複数回プルされるのを防ぐためです。それは通常の、確立された慣行です。

sourceファイルに冗長インクルードガードを配置する理由は、含まれているヘッダーファイルを開いたり、コンパイルを大幅に高速化できる昔に戻ったりする必要がないようにするためです。最近では、ファイルを開くのが以前よりずっと速くなりました。さらに、コンパイラーは既に見たファイルを覚えるのが非常に賢く、インクルードガードイディオムを理解しているので、ファイルを再度開く必要がないことを自分で理解できます。これは少し手作業ですが、要点は、この余分なレイヤーはもう必要ないということです。

編集:ここでの別の要因は、C++のコンパイルがfar Cのコンパイルよりも複雑であるため、far時間がかかり、インクルードファイルを開くのにかかる時間がより小さく、重要でない部分になることです翻訳単位のコンパイルにかかる時間の.

49
Pete Becker

私が思いつくことができる唯一の利点は、リンカーがファイルを見つけるのを煩わせることを完全に止めることです。

リンカは影響を受けません。

pre-processorがファイルを見つけるのを邪魔するのを防ぐことができますが、ガードが定義されている場合、それはすでにファイルを見つけていることを意味します。前処理時間がまったく短縮された場合、最も病理学的に再帰的に含まれる怪物を除き、その影響は非常に小さいと思われます。

ガードが変更された場合(たとえば、別のガードとの競合が原因)、インクルードディレクティブの前にあるすべての条件を変更して動作させる必要があるという欠点があります。そして、他の何かが以前のガードを使用している場合、includeディレクティブ自体が正しく機能するように条件を変更する必要があります。

追伸__HEADER_A_HPP__は実装専用のシンボルであるため、定義できるものではありません。ガードには別の名前を使用してください。

22
eerorika

より伝統的な(メインフレーム)プラットフォーム上の古いコンパイラ(ここでは2000年半ばの話です)では、他の回答で説明されている最適化を使用していなかったため、ヘッダーファイルを再読み込みする前処理時間を大幅に遅くしていました既に含まれています(多くのヘッダーファイルを含める予定の、大規模でモノリシックな企業向けプロジェクトに留意してください)。例として、AIXコンパイラーのVisualAge C++ 6(2000年代半ば以降)の同じ256個のヘッダーファイルをそれぞれ含む256個のヘッダーファイルを持つファイルの26倍の高速化を示すデータを見ました。これはかなり極端な例ですが、この種の高速化は合計されます。

ただし、最新のコンパイラはすべて、AIXやSolarisなどのメインフレームプラットフォーム上でも、ヘッダーを含めるために十分な最適化を実行するため、最近の違いはごくわずかです。したがって、これらをこれ以上持つ正当な理由はありません。

ただし、比較的最近(少なくともC/C++コードベースの時代では)非常に大規模なモノリシックプロジェクトに価値があったため、一部の企業がまだこの慣行に固執している理由を説明しています。

17
Muzer

これに反対する人もいますが、実際には「#pragma once」は完全に機能し、メインコンパイラ(gcc/g ++、vc ++)がそれをサポートしています。

したがって、人々が広めている純粋主義的な議論が何であれ、それははるかにうまく機能します:

  1. 速い
  2. 古いフラグをコピーしたため、メンテナンスや不思議な包含の問題はありません
  3. 明確な意味を持つ単一行とファイル内に広がる不可解な行

簡単に言えば:

#pragma once

ファイルの先頭で、それだけです。最適化され、保守可能で、すぐに使用できます。

8
Bert Bril