web-dev-qa-db-ja.com

typedefのヘッダーファイルのベストプラクティス

私はプロジェクトでshared_ptrとSTLを広範に使用していますが、これはshared_ptr< vector< shared_ptr<const Foo> > >(私は好みによりObjCプログラマーであり、長い名前が標準であり、それでもこれは多すぎる。)一貫してこれをFooListPtrと呼んで文書化することはずっと明確だと思う「Ptr」はshared_ptrを意味し、「リスト」はshared_ptrのベクトルを意味する命名規則。

これは簡単にtypedefできますが、ヘッダーで頭痛の種を引き起こしています。 FooListPtrを定義する場所にはいくつかのオプションがあるようです。

  • Foo.h.それはすべてのヘッダーに絡み合い、深刻なビルドの問題を引き起こすため、スターターではありません。
  • FooFwd.h(「フォワードヘッダー」)。これは、iosfwd.hに基づいてEffective C++が提案するものです。非常に一貫性がありますが、ヘッダーの数を2倍に維持するオーバーヘッドは、せいぜい面倒です。
  • Common.h(それらすべてを1つのファイルにまとめます)。これは、多くの無関係な型を絡めて再利用性を損ないます。 1つのオブジェクトを選択して別のプロジェクトに移動することはできません。それは非スターターです。
  • ある種の#defineマジックは、typedefがまだtypedefされていない場合にtypedefのようになります。新しい人がコードを理解するのが難しくなると思うので、私はプリプロセッサが常に嫌いです。
  • Typedefではなくベクターサブクラスを使用します。これは危険なようです...

ここにベストプラクティスはありますか?再利用性、読みやすさ、一貫性が最重要である場合、実際のコードではどのようになりますか?

他の人が議論のために追加のオプションを追加したい場合、このコミュニティwikiにマークを付けました。

51
Rob Napier

私はcommon.hメソッドを使用しているように聞こえるプロジェクトでプログラミングしています。そのプロジェクトでは非常にうまく機能します。

プリコンパイル済みヘッダーにあり、すべての重要なクラスと必要なtypedefを単純に前方宣言するForwardsDecl.hというファイルがあります。この場合、unique_ptrの代わりにshared_ptrが使用されますが、使用方法は同様でなければなりません。次のようになります。

// Forward declarations
class ObjectA;
class ObjectB;
class ObjectC;

// List typedefs
typedef std::vector<std::unique_ptr<ObjectA>> ObjectAList;
typedef std::vector<std::unique_ptr<ObjectB>> ObjectBList;
typedef std::vector<std::unique_ptr<ObjectC>> ObjectCList;

このコードは、クラスが前方宣言されているだけでもVisual C++ 2010で受け入れられます(完全なクラス定義は必要ないため、各クラスのヘッダーファイルを含める必要はありません)。それが標準であり、他のコンパイラが完全なクラス定義を必要とするかどうかはわかりませんが、そうしないと便利です:別のクラス(ObjectD)はObjectAListをメンバーとして持つことができ、ObjectA.hを含める必要はありません-これはできますヘッダーファイルの依存関係を減らすのに本当に役立ちます!

フォワード宣言は1回だけ記述する必要があり、その後の変更はクラスのヘッダーファイル内の完全な宣言でのみ行う必要があるため、メンテナンスは特に問題ではありません(これにより、縮小のために再コンパイルされるソースファイルが少なくなります)依存関係)。

最後に、これはプロジェクト間で共有できるようです(私は自分で試したことはありません)気にしません。したがって、ファイルには、使用されているすべてのプロジェクトのクラスの名前を含めることができ、特定のプロジェクトで一部が欠落していても問題ありません。必要なのは、必要な完全な宣言ヘッダー(たとえば、ObjectA.h)が実際に使用するであるすべてのsource(.cpp)ファイルに含まれていることです。

13
AshleysBrain

フォワードヘッダーと一種のcommon.hプロジェクトに固有のヘッダー。すべての前方宣言ヘッダーと、一般的で軽量なその他のものをすべて含みます。

ヘッダーの2倍の数を維持するオーバーヘッドについて不平を言うが、これはあまり大きな問題ではないと思う:フォワードヘッダーは通常、非常に限られた数のタイプ(1つ?)フルタイプ。

reallyがある場合は、スクリプトを使用してヘッダーを自動生成することもできます(これは、たとえば SeqAn で行われます)多くのヘッダー。

6
Konrad Rudolph

typedef規則を文書化するための+1。

  • Foo.h-あなたが持っている問題を詳しく説明できますか?
  • FooFwd.h-それらを一般的には使用せず、「明らかなホットスポット」でのみ使用します。 (はい、「ホットスポット」are判断が困難です)。 fwdヘッダーを導入すると、関連するfoo.hのtypedefがそこに移動するため、ルールIMOは変更されません。
  • Common.h-小さなプロジェクトにはクールですが、スケールしません、私は同意します。
  • ある種の空想#define... NO PLEASE NO!...
  • ベクターサブクラスを使用する-改善されません。ただし、封じ込めを使用することもできます。

そこで、ここで予備的な提案(他の質問から改訂された..)

  1. 標準型ヘッダー<boost/shared_ptr.hpp><vector>などは、プロジェクトのプリコンパイル済みヘッダー/共有インクルードファイルに入ることができます。 これは悪くない。(私は個人的には必要に応じてそれらを含めるが、それはPCHに入れることに加えて機能する。)

  2. コンテナが実装の詳細である場合、typedefはコンテナが宣言されている場所に移動します(たとえば、コンテナがプライベートクラスメンバーの場合はプライベートクラスメンバー)

  3. 関連付けられた型(FooListPtrなど)は、Fooが宣言されている場所に移動します。if関連付けられた型は、その型の主な用途です。それはほとんどの場合、一部のタイプに当てはまります-例えばshared_ptr

  4. Fooが個別の前方宣言ヘッダーを取得し、関連する型がそれで問題ない場合、FooFwd.hにも移動します。

  5. 型が特定のインターフェイス(たとえば、パブリックメソッドのパラメーター)にのみ関連付けられている場合は、そこに移動します。

  6. タイプが共有されている場合(および前述の基準のいずれも満たさない場合)、独自のヘッダーを取得します。これは、すべての依存関係を取り込むことも意味することに注意してください。

私には「明らか」だと感じていますが、コーディング標準としては良くないことに同意します。

4
peterchen

私はプロジェクトでshared_ptrとSTLを広範に使用していますが、これはshared_ptr <vector <shared_ptr>>のような、エラーが発生しやすい長すぎるタイプにつながります(私は好みによりObjCプログラマーです。私は、このFooListPtrを一貫して呼び出して、「Ptr」はshared_ptrを意味し、「List」はshared_ptrのベクトルを意味するという命名規則を文書化することは、はるかに明確だと思います。

手始めに、スコーピング(名前空間など)に適した設計構造、およびtypedefの記述的で短縮されていない名前を使用することをお勧めします。 FooListPtrは非常に短いです。略語の意味を推測したい人はいません(または、Fooがconst、sharedなどであることに驚くこともありません)。

ライブラリ(および他の一般的なカテゴリ)でtypedefのプレフィックスを選択することも役立ちます。

また、宣言されたスコープから型をドラッグすることもお勧めしません。

namespace MON {
namespace Diddy {
class Foo;
} /* << Diddy */

/*...*/
typedef Diddy::Foo Diddy_Foo;

} /* << MON */

これには例外があります:

  • 完全にカプセル化されたプライベートタイプ
  • 新しいスコープ内の包含型

その間、名前空間スコープと名前空間エイリアスのusingは避けるべきです-将来の保守を最小限にしたい場合は、スコープを修飾してください。

これは簡単にtypedefできますが、ヘッダーで頭痛の種を引き起こしています。 FooListPtrを定義する場所にはいくつかのオプションがあるようです。

Foo.h.それはすべてのヘッダーに絡み合い、深刻なビルドの問題を引き起こすため、スターターではありません。

他の宣言に実際に依存する宣言のオプションかもしれません。パッケージを分割する必要があること、またはサブシステム用の共通のローカライズされたインターフェイスがあることを意味します。

FooFwd.h(「フォワードヘッダー」)。これは、iosfwd.hに基づいて、Effective C++が提案するものです。非常に一貫性がありますが、ヘッダーの数を2倍に維持するオーバーヘッドは、せいぜい面倒です。

本当にこれのメンテナンスを心配しないでください。それは良い習慣です。コンパイラは、わずかな労力で前方宣言とtypedefを使用します。依存関係を減らし、それらがすべて正しく表示されることを保証するのに役立つため、迷惑ではありません。他のファイルは「パッケージタイプ」ヘッダーを参照するため、実際にはこれ以上のメンテナンスは必要ありません。

Common.h(それらすべてを1つのファイルにまとめます)。これは、多くの無関係な型を絡めて再利用性を損ないます。 1つのオブジェクトを選択して別のプロジェクトに移動することはできません。それは非スターターです。

パッケージベースの依存関係とインクルージョンは優れています(実際に理想的です)-これを除外しないでください。もちろん、適切に設計および構成され、関連するコンポーネントのクラスを表すパッケージインターフェイス(またはライブラリ)を作成する必要があります。オブジェクト/コンポーネントの再利用から不必要な問題を引き起こしています。ライブラリの静的データを最小化し、リンクフェーズとストリップフェーズにジョブを実行させます。繰り返しますが、パッケージを小さく再利用可能にしておくと、これは問題になりません(ライブラリ/パッケージが適切に設計されていると仮定します)。

ある種の#defineマジックは、typedefがまだtypedefされていない場合にtypedefのようになります。新しい人がコードを理解するのが難しくなると思うので、私はプリプロセッサが常に嫌いです。

実際には、同じスコープ内でtypedefを複数回(たとえば、2つの別々のヘッダーで)宣言できます-これはエラーではありません。

同じスコープでtypedefを異なる型で宣言するisエラー。明らかに。これを回避する必要があり、幸いなことにコンパイラはそれを強制します。

これを回避するには、世界を含む「翻訳ビルド」を作成します-コンパイラは、一致しないtypedeffed型の宣言にフラグを立てます。

最小限のtypedefや転送(またはコンパイル時に解放するのに十分近い)でこっそりとやってみるのは努力の価値がありません。場合によっては、前方宣言の条件付きサポートの束が必要になります-定義したら、それは簡単です(stlライブラリはこの良い例です-template<typename,typename>class vector;を前方宣言している場合)。

エラーをすぐにキャッチするには、これらの宣言をすべて表示するのが最善です。この場合、ボーナスとしてプリプロセッサを回避できます。

Typedefではなくベクターサブクラスを使用します。これは危険なようです...

std::vectorのサブクラスは、しばしば「初心者の間違い」としてフラグが立てられます。このコンテナはサブクラス化されることを意図していませんでした。コンパイル時間/依存関係を減らすためだけに悪い習慣に頼らないでください。依存関係が本当にそれほど重要な場合は、おそらくPIMPLを使用する必要があります。

// <package>.types.hpp
namespace MON {
class FooListPtr;
}

// FooListPtr.hpp
namespace MON {
class FooListPtr {
    /* ... */
private:
    shared_ptr< vector< shared_ptr<const Foo> > > d_data;
};
}

ここにベストプラクティスはありますか?再利用性、読みやすさ、一貫性が最重要である場合、実際のコードではどのようになりますか?

最終的に、再利用、コンパイル時間の短縮、および依存性の最小化に最適な小さな簡潔なパッケージベースのアプローチを見つけました。

2
justin

残念ながら、typedefを使用すると、ヘッダーファイルに最適でないオプションを選択する必要があります。オプション1(クラスヘッダー内)が適切に機能する特別なケースがありますが、それはあなたには機能しないようです。最後のオプションが適切に機能する場合もありますが、通常は、サブクラスを使用して、クラスを含むパターンをstd :: vector型の単一のメンバーに置き換えます。あなたの状況では、前方宣言ヘッダーソリューションを使用します。追加のタイピングとオーバーヘッドがありますが、そうでなければC++ではありませんよね?物事を分離し、クリーンで高速に保ちます。

1
Michael Daum