43

これは、同じ前処理命令に対する複数の質問です。

1 - <> または "" ?

MSDN にある情報とは別に:

#include ディレクティブ (C-C++)

1.a: 2 つの表記法の違いは何ですか?
1.b: すべてのコンパイラが同じように実装していますか?
1.c: いつ <> を使用し、いつ "" を使用しますか?

2 - #include {TheProject/TheHeader.hpp} または {TheHeader.hpp} ?

私は、自分のプロジェクト ヘッダーのインクルードを記述する方法を少なくとも 2 つ見てきました。少なくとも 4 種類のヘッダーがあることを考慮すると、次のようになります。

  • あなたのプロジェクトのプライベートヘッダー?
  • プロジェクトのヘッダーですが、シンボルをエクスポートしています(したがって、「パブリック」)
  • モジュールがリンクする別のプロジェクトのヘッダー
  • コンパイラまたは標準ライブラリのヘッダー

ヘッダーの種類ごとに:

2.a: <> または "" のどちらを使用しますか?
2.b: {TheProject/TheHeader.hpp} と一緒に含めますか、それとも {TheHeader.hpp} のみと一緒に含めますか?

3 - ボーナス

3.a: ツリーのような組織 (つまり、「1 つのディレクトリ内のすべてのファイル」ではなく、ディレクトリ内のディレクトリ) 内のソースおよび/またはヘッダーを使用してプロジェクトに取り組んでいますか? また、長所/短所は何ですか?

4

10 に答える 10

35

すべての回答とコンパイラのドキュメントを読んだ後、次の標準に従うことにしました。

プロジェクトヘッダーまたは外部ヘッダーであるすべてのファイルに対して、常に次のパターンを使用します。

#include <namespace/header.hpp>

衝突を避けるために、名前空間は少なくとも 1 つのディレクトリの深さになります。

もちろん、これは、プロジェクト ヘッダーが配置されているプロジェクト ディレクトリを「デフォルトのインクルード ヘッダー」としてメイクファイルにも追加する必要があることを意味します。

この選択の理由は、次の情報を見つけたからです。

1. インクルード "" パターンはコンパイラに依存します

私は以下の答えを与えます

1.a 標準

ソース:

  • C++14 ワーキング ドラフト n3797 : https://isocpp.org/files/papers/N3797.pdf
  • C++11、C++98、C99、C89 (引用されたセクションは、これらすべての標準で変更されていません)

セクション 16.2 ソース ファイル インクルードでは、次のことがわかります。

フォームの前処理ディレクティブ

  #include <h-char-sequence> new-line

< と > 区切り文字の間の指定されたシーケンスによって一意に識別されるヘッダーの実装定義の場所のシーケンスを検索し、そのディレクティブをヘッダーの内容全体で置き換えます。場所を指定する方法またはヘッダーを識別する方法は実装定義です。

これは、 #include <...> が実装定義の方法でファイルを検索することを意味します。

次に、次の段落:

フォームの前処理ディレクティブ

  #include "q-char-sequence" new-line

このディレクティブは、" デリミタ間の指定されたシーケンスで識別されるソース ファイルの内容全体に置き換えられます。指定されたソース ファイルは、実装定義の方法で検索されます。この検索がサポートされていない場合、または検索が失敗した場合は、 、ディレクティブは読み取ったかのように再処理されます

  #include <h-char-sequence> new-line

元のディレクティブからの同一の含まれるシーケンス (存在する場合は > 文字を含む) を使用します。

これは、#include "..." が実装定義の方法でファイルを検索し、ファイルが見つからない場合、#include <...> であるかのように別の検索を行うことを意味します。

結論は、コンパイラのドキュメントを読む必要があるということです。

何らかの理由で、標準のどこにも「システム」または「ライブラリ」ヘッダーまたはその他のヘッダーの違いがないことに注意してください。唯一の違いは、#include <...> はヘッダーをターゲットにしているように見えますが、#include "..." はソースをターゲットにしているようです (少なくとも、英語の表現では)。

1.b ビジュアル C++:

ソース:

#include "MyFile.hpp"

プリプロセッサは、次の順序でインクルード ファイルを検索します。

  1. #include ステートメントを含むファイルと同じディレクトリ。
  2. 以前に開いたインクルード ファイルのディレクトリで、開いたときと逆の順序で。検索は、最後に開かれたインクルード ファイルのディレクトリから開始され、最初に開かれたインクルード ファイルのディレクトリまで続きます。
  3. 各 /I コンパイラ オプションで指定されたパスに沿って。
  4. (*) INCLUDE 環境変数または開発環境のデフォルト インクルードで指定されたパスに沿って。

#include <MyFile.hpp>

プリプロセッサは、次の順序でインクルード ファイルを検索します。

  1. 各 /I コンパイラ オプションで指定されたパスに沿って。
  2. (*) INCLUDE 環境変数または開発環境のデフォルト インクルードで指定されたパスに沿って。

最後のステップについての注意

ドキュメントは、 と include の両方の「INCLUDE 環境変数で指定されたパスに沿って」部分について明確ではありませ<...>"..."。次の引用により、標準に準拠しています。

#include "path-spec" として指定されたインクルード ファイルの場合、ディレクトリ検索は親ファイルのディレクトリから開始され、次にすべての親の親ファイルのディレクトリに進みます。つまり、検索は、処理中の #include ディレクティブを含むソース ファイルを含むディレクトリに対して相対的に開始されます。祖父母ファイルがなく、ファイルが見つからない場合は、ファイル名が山かっこで囲まれているかのように検索が続行されます。

したがって、最後のステップ (アスタリスクでマークされている) は、ドキュメント全体を読んだ結果の解釈です。

1.cg++

ソース:

次の引用は、プロセスを要約しています。

GCC [...] は #include<file>で要求されたヘッダーを [システム ディレクトリ] で検索します [...] -I で指定されたすべてのディレクトリは、デフォルト ディレクトリの前に左から右の順序で検索されます

GCC は #include "file" で要求されたヘッダーを最初に現在のファイルを含むディレクトリで検索し、次に -iquote オプションで指定されたディレクトリで検索します。次に、山かっこで要求されたヘッダーを検索した場合と同じ場所で検索します。

#include "MyFile.hpp"

このバリアントは、独自のプログラムのヘッダー ファイルに使用されます。プリプロセッサは、次の順序でインクルード ファイルを検索します。

  1. #include ステートメントを含むファイルと同じディレクトリ。
  2. 各 -iquote コンパイラ オプションで指定されたパスに沿って。
  3. #include について<MyFile.hpp>

#include <MyFile.hpp>

このバリアントは、システム ヘッダー ファイルに使用されます。プリプロセッサは、次の順序でインクルード ファイルを検索します。

  1. 各 -I コンパイラ オプションで指定されたパスに沿って。
  2. システムディレクトリ内。

1.d Oracle/Sun Studio CC

ソース:

テキスト自体が多少矛盾していることに注意してください (理解するには例を参照してください)。キー フレーズは次のとおりです。「違いは、名前が引用符で囲まれたヘッダー ファイルのみが現在のディレクトリで検索されることです。

#include "MyFile.hpp"

このバリアントは、独自のプログラムのヘッダー ファイルに使用されます。プリプロセッサは、次の順序でインクルード ファイルを検索します。

  1. 現在のディレクトリ (つまり、「include」ファイルを含むディレクトリ)
  2. -I オプションで指定されたディレクトリ (存在する場合)
  3. システムディレクトリ (例: /usr/include ディレクトリ)

#include <MyFile.hpp>

このバリアントは、システム ヘッダー ファイルに使用されます。プリプロセッサは、次の順序でインクルード ファイルを検索します。

  1. -I オプションで指定されたディレクトリ (存在する場合)
  2. システムディレクトリ (例: /usr/include ディレクトリ)

1.e XL C/C++ コンパイラ リファレンス - IBM/AIX

ソース:

両方の文書のタイトルは「XL C/C++ Compiler Reference」です。最初の文書は古い (8.0) ですが、理解しやすいものです。2 つ目は新しい (12.1) ですが、復号化が少し難しくなります。

#include "MyFile.hpp"

このバリアントは、独自のプログラムのヘッダー ファイルに使用されます。プリプロセッサは、次の順序でインクルード ファイルを検索します。

  1. 現在のディレクトリ (つまり、「include」ファイルを含むディレクトリ)
  2. -I オプションで指定されたディレクトリ (存在する場合)
  3. システムディレクトリ (例: /usr/vac[cpp]/include または /usr/include ディレクトリ)

#include <MyFile.hpp>

このバリアントは、システム ヘッダー ファイルに使用されます。プリプロセッサは、次の順序でインクルード ファイルを検索します。

  1. -I オプションで指定されたディレクトリ (存在する場合)
  2. システム ディレクトリ (例: /usr/vac[cpp]/include または /usr/include ディレクトリ)

1.e 結論

パターン "" はコンパイラ間で微妙なコンパイル エラーを引き起こす可能性があり、現在 Windows Visual C++、Linux g++、Oracle/Solaris CC、および AIX XL の両方で作業しているため、これは受け入れられません。

とにかく、"" で説明されている機能の利点はとにかく面白くないので...

2. {namespace}/header.hpp パターンを使用する

私は職場で見ました (つまり、これは理論ではありません。これは現実の、つらい専門的経験です)。同じ名前の 2 つのヘッダーがあり、1 つはローカル プロジェクト ディレクトリにあり、もう 1 つはグローバル インクルードにあります。

"" パターンを使用していて、そのファイルがローカル ヘッダーとグローバル ヘッダーの両方に含まれていたため、奇妙なエラーが表示されたときに、実際に何が起こっているのかを理解する方法がありませんでした。

インクルードでディレクトリを使用すると、ユーザーが次のいずれかを記述する必要があったため、時間を節約できました。

#include <MyLocalProject/Header.hpp>

また

#include <GlobalInclude/Header.hpp>

その間、あなたはそれに気付くでしょう

#include "Header.hpp"

正常にコンパイルされるため、問題はまだ隠されていますが、

#include <Header.hpp>

通常の状況ではコンパイルされません。

したがって、<> 表記に固執すると、開発者は適切なディレクトリを含むインクルードのプレフィックスを付けることが必須になり、"" よりも <> を好むもう 1 つの理由になります。

3. 結論

<> 表記と名前空間表記の両方を一緒に使用すると、デフォルトのインクルード ディレクトリのみを検索する代わりに、プリコンパイラからファイルを推測する可能性がなくなります。

もちろん、標準ライブラリは通常どおり含まれています。つまり、次のとおりです。

#include <cstdlib>
#include <vector>
于 2009-08-09T12:37:50.090 に答える
7

通常、システム ヘッダーには <> を使用し、プロジェクト ヘッダーには "" を使用します。パスに関しては、必要なファイルがインクルード パスのサブディレクトリにある場合にのみ必要です。

たとえば、/usr/include/SDL/ にファイルが必要であるが、インクルード パスに /usr/include/ しかない場合は、次を使用できます。

#include <SDL/whatever.h>

また、パスが / で始まらない限り、現在の作業ディレクトリからの相対パスであることに注意してください。

編集してコメントに答える:ライブラリのインクルードが数個しかない場合は、そのサブディレクトリをインクルードパスに含めるだけですが、ライブラリに多くのヘッダー(数十個など)がある場合は、私が指定したサブディレクトリにあります。これの良い例は、Linux のシステム ヘッダーです。次のように使用します。

#include <sys/io.h>
#include <linux/limits.h>

別の良い答えを含めるように編集してください:また、2つ以上のライブラリが同じ名前でヘッダーを提供することが考えられる場合、サブディレクトリソリューションは基本的に各ヘッダーに名前空間を与えます。

于 2008-10-07T16:15:20.680 に答える
5

C99 標準から引用するには (一見すると、C90 標準の文言は同じように見えますが、そこからカットアンドペーストすることはできません):

フォームの前処理ディレクティブ

# include "q-char-sequence" new-line

このディレクティブは、" デリミタ間の指定されたシーケンスで識別されるソース ファイルの内容全体に置き換えられます。指定されたソース ファイルは、実装定義の方法で検索されます。この検索がサポートされていない場合、または検索が失敗した場合は、 、ディレクティブは読み取ったかのように再処理されます

# include <h-char-sequence> new-line

元のディレクティブからの同一の含まれるシーケンス (存在する場合は > 文字を含む) を使用します。

したがって、 によって検索された場所は、 によって検索され#include "whatever"た場所のスーパーセットです#include <whatever>。その意図は、最初のスタイルが一般にあなたに「属する」ヘッダーに使用され、2 番目の方法がコンパイラ/環境に「属する」ヘッダーに使用されることです。もちろん、多くの場合、いくつかの灰色の領域があります。たとえば、Boost ヘッダーにはどれを使用する必要がありますか? 私は を使用#include <>しますが、チームの他の誰かが を望んでいる場合は、あまり議論しません#include ""

実際には、ビルドが壊れない限り、どのフォームが使用されているかにあまり注意を払う人はいないと思います。私は確かに、コードレビューで言及されたことを覚えていません(またはそうでなければ)。

于 2008-10-07T17:01:56.670 に答える
3

質問の 2 番目の部分に取り組みます。

通常<project/libHeader.h>、サードパーティのヘッダーを含めるときに使用します。また"myHeader.h"、プロジェクト内からヘッダーを含める場合。

<project/libHeader.h>代わりに使用する理由<libHeader.h>は、複数のライブラリに「libHeader.h」ファイルがある可能性があるためです。両方を含めるには、含めるファイル名の一部としてライブラリ名が必要です。

于 2008-10-07T16:17:33.687 に答える
2

1.a: 2 つの表記法の違いは何ですか?

"" は、C/C++ ファイルが配置されているディレクトリで検索を開始します。<> は、-I ディレクトリとデフォルトの場所 (/usr/include など) で検索を開始します。どちらも最終的には同じ場所のセットを検索しますが、順序のみが異なります。

1.b: すべてのコンパイラが同じように実装していますか?

そう願っていますが、よくわかりません。

1.c: いつ <> を使用し、いつ "" を使用しますか?

インクルード ファイルが C ファイルの隣にある場合は "" を使用し、それ以外の場合は <> を使用します。特に、私たちのプロジェクトでは、すべての「パブリック」インクルード ファイルが project/include ディレクトリにあるため、<> を使用しています。

2 - #include {TheProject/TheHeader.hpp} または {TheHeader.hpp} ?

すでに指摘したように、xxx/filename.h では、diskio/ErrorCodes.h や netio/ErrorCodes.h などを実行できます。

* プロジェクトのプライベート ヘッダー?

プロジェクト内のサブシステムのプライベート ヘッダー。プロジェクト内のサブシステムの "filename.h" パブリック ヘッダーを使用します (プロジェクトの外部では表示されませんが、他のサブシステムからはアクセスできます)。プロジェクトに適用された規則に応じて、 または を使用します。むしろ使いたい

* プロジェクトのヘッダーですが、シンボルをエクスポートしています (つまり、「パブリック」)

ライブラリのユーザーがそれらを含めるのとまったく同じように含めます。おそらく

* モジュールがリンクする別のプロジェクトのヘッダー

プロジェクトによって決定されますが、確かに <> * コンパイラまたは標準ライブラリのヘッダーを使用しています。

3.a: ツリーのような組織 (つまり、「1 つのディレクトリ内のすべてのファイル」ではなく、ディレクトリ内のディレクトリ) 内のソースおよび/またはヘッダーを使用してプロジェクトに取り組んでいますか? また、長所/短所は何ですか?

私は構造化されたプロジェクトに取り組んでいます。ファイルの数が 20 を超えるとすぐに、いくつかの分割が明らかになります。コードがあなたを引っ張っている方法で行く必要があります。

于 2008-10-07T17:55:20.510 に答える
1

Re <>vs""。私の店では、「スタイル」の問題に関しては、非常に手間がかかります。私が要件を持っている数少ない領域の1つは、#includeステートメントで山かっこを使用することです。ルールは次のとおりです。オペレーティングシステムまたはコンパイラファイルを#includeしている場合は、必要に応じて山かっこを使用できます。他のすべての場合、それらは禁止されています。ここに誰かが書いたファイルまたはサードパーティのライブラリを#includeしている場合、<>は禁止されています。

理由は次のとおりです。#include"xh"と#includeは同じパスを検索しません。#includeは、システムパスと、-iを入力したものだけを検索します。重要なことに、ファイルxhが他の方法で検索パスに含まれていない場合、そのディレクトリは検索されません。

たとえば、次のファイルがあるとします。

c:\ dev \ angles \ main.cpp

#include "c:\utils\mylib\mylibrary.h"

int main()
{
    return 0;
}

c:\ utils \ mylib \ mylibrary.h

#ifndef MYLIB_H
#define MYLIB_H

#include <speech.h>

namespace mylib
{
    void Speak(SpeechType speechType);  
};

#endif

c:\ utils \ mhlib \ Speech.h

#ifndef SPEECH_H
#define SPEECH_H

namespace mylib
{
    enum SpeechType {Bark, Growl};
};

#endif

これは、PATH環境変数を設定するか、c:\ utils \ mhlib \ディレクトリで-iを実行して、パスを変更しないとコンパイルされません。そのファイルが!#include <speech.h>と同じディレクトリにある場合でも、コンパイラは削除できません。mylibrary.h

2つの理由から、コードの#includeステートメントで相対パス名と絶対パス名を多用しています。

1)ライブラリとコンポーネントをメインのソースツリーから除外する(つまり、ユーティリティライブラリを特別なディレクトリに配置する)ことにより、ライブラリのライフサイクルをアプリケーションのライフサイクルに結合しません。これは、共通のライブラリを使用するいくつかの異なる製品がある場合に特に重要です。

2)ジャンクションを使用して、ハードドライブ上の物理的な場所を論理ドライブ上のディレクトリにマップし、すべての#includeで論理ドライブ上の完全修飾パスを使用します。例えば:

#include "x:\utils\mylib.h"--good、x:はsubstされたドライブであり、x:\ utilsはハードドライブ上のc:\ code\utils_1.0を指します

#include "c:\utils_1.0\mylib.h" - 悪い!アプリケーションthat#includes mylib.hは、MYLIBライブラリの特定のバージョンに結合され、すべての開発者は、ハードドライブの同じディレクトリc:\utils_1.0にそれを持っている必要があります。

最後に、私のチームの目標を達成するのは広いですが難しいのは、ワンクリックコンパイルをサポートできるようにすることです。これには、ソース管理からコードを取得してから「コンパイル」を押すだけで、メインのソースツリーをコンパイルできることが含まれます。特に、コンパイルできるようにするためにパスとマシン全体の#includeディレクトリを設定する必要があるのは嫌です。なぜなら、開発マシンのビルドでセットアップフェーズに追加する小さなステップごとに、混乱が難しくなり、混乱しやすくなるからです。新しいマシンを高速化してコードを生成するのに時間がかかります。

于 2008-10-07T18:38:08.163 に答える
1

私の記憶が正しければ。

「パス」にあるすべてのライブラリにひし形を使用します。したがって、STL にあるライブラリ、またはインストールしたライブラリ。Linux ではパスは通常 "/usr/include" ですが、Windows ではよくわかりませんが、"C:\windows" の下にあると思います。

"" を使用して、他のすべてを指定します。開始ディレクトリ情報のない「my_bla.cpp」は、コードが存在/コンパイルしているディレクトリに解決されます。または、インクルードの正確な場所を指定することもできます。この「c:\myproj\some_code.cpp」のように

ヘッダーの種類は関係ありません。場所だけです。

于 2008-10-07T16:14:06.753 に答える
1

と の間には主に 2 つの違いが<>あり""ます。1 つ目は、名前を終了する文字です。ヘッダー名にはエスケープ シーケンスがないため、#include <bla"file.cpp>または"bla>file.cpp". しかし、それはおそらく頻繁には出てこないでしょう。もう 1 つの違いは、システム インクルードは では発生しないはずであるということ""です<>。したがって#include "iostream"、動作が保証されているわけではありません。#include <iostream>は。私の個人的な好みは""、プロジェクトの一部である<>ファイルとそうでないファイルに使用することです。一部の人々は<>、標準ライブラリのヘッダーのみに使用し、""その他すべてに使用します。<>Boost と std のみを使用する人もいます。プロジェクトによって異なります。すべてのスタイルの側面と同様に、最も重要なことは一貫性を保つことです。

パスに関しては、外部ライブラリがヘッダーの規則を指定します。例えば<boost/preprocessor.hpp> <wx/window.h> <Magic++.h>。ローカル プロジェクトでは、最上位の srcdir (または、それらが異なるライブラリ プロジェクトでは、インクルード ディレクトリ) に関連するすべてのパスを記述します。

ライブラリを作成するときは、 <> を使用してプライベート ヘッダーとパブリック ヘッダーを区別したり-I、ソース ディレクトリではなく上のディレクトリを区別したりすることも役立つ場合が#include "public_header.hpp"あり"src/private_header.hpp"ます。それは本当にあなた次第です。

編集: ディレクトリ構造を持つプロジェクトについては、強くお勧めします。すべてのブーストが 1 つのディレクトリにある (サブ名前空間がない) と想像してみてください! "module\_text\_processor.hpp"ディレクトリ構造は、ファイルの検索が容易になり、(とは対照的に)命名の柔軟性が向上するため、優れています"module/text\_processor.hpp"。後者はより自然で使いやすいです。

于 2008-10-07T16:17:14.360 に答える
0

システム ヘッダー ファイル (stdio、iostreams、string など) の <...> を使用し、そのプロジェクトに固有のヘッダーには "..." を使用します。

于 2008-10-07T16:15:29.993 に答える
0

プロジェクトにローカルなヘッダーには #include "header.h" を使用し、ソリューション内のシステム インクルード、サード パーティ インクルード、およびその他のプロジェクトには #include を使用します。Visual Studio を使用していますが、ヘッダー インクルードでプロジェクト ディレクトリを使用する方がはるかに簡単です。この方法では、新しいプロジェクトを作成するたびに、すべてのプロジェクト ディレクトリを含むディレクトリのインクルード パスを指定するだけで済みます。各プロジェクト。

于 2008-10-07T17:39:10.293 に答える