web-dev-qa-db-ja.com

C ++ライブラリとフレームワークがスマートポインターを使用しないのはなぜですか?

生のポインターを使用することはほとんどないという記事をいくつか読みました。代わりに、スコープ付きスコープまたは共有ポインターに関係なく、スマートポインターで常にラップする必要があります。

しかし、Qt、wxWidgets、Boostなどのライブラリのようなフレームワークは、それらをまったく使用していないかのようにスマートポインターを返さず、期待もしていません。代わりに、生のポインタを返すか予期します。その理由はありますか?パブリックAPIを記述するとき、スマートポインターから離れる必要がありますか?

多くの主要プロジェクトがスマートポインターを避けているように見えるのに、なぜスマートポインターが推奨されるのか疑問に思います。

156
laurent

多くのライブラリが標準のスマートポインタの出現前に作成されたという事実は別として、最大の理由はおそらく標準C++ Application Binary Interface(ABI)の欠如です。

ヘッダーのみのライブラリを作成している場合は、スマートポインターと標準コンテナーを心ゆくまで渡すことができます。それらのソースはコンパイル時にライブラリで利用できるため、実装ではなくインターフェイスの安定性のみに依存します。

しかし、標準のABIがないため、通常はcannotモジュール境界を越えてこれらのオブジェクトを安全に渡します。 GCC shared_ptrは、おそらくMSVCとは異なりますshared_ptr、これもIntelとは異なりますshared_ptrsameコンパイラを使用しても、これらのクラスはバージョン間でバイナリ互換性があるとは限りません。

一番下の行は、ライブラリのprebuiltバージョンを配布する場合、依存する標準ABIが必要であるということです。 Cにはありませんが、コンパイラベンダーは特定のプラットフォームのCライブラリ間の相互運用性について非常に優れています。事実上の標準があります。

この状況はC++にはあまり良くありません。個々のコンパイラは独自のバイナリ間の相互運用を処理できるため、サポートされているすべてのコンパイラ(多くの場合GCCおよびMSVC)にバージョンを配布するオプションがあります。しかし、これを考慮して、ほとんどのライブラリはCインターフェイスをエクスポートするだけです。つまり、生のポインタを意味します。

ただし、通常、非ライブラリコードはrawよりもスマートポインターを優先する必要があります。

123
Jon Purdy

多くの理由が考えられます。それらのいくつかをリストするには:

  1. スマートポインターは、最近標準の一部になりました。それまでは、他のライブラリの一部でした
  2. 主な用途は、メモリリークを回避することです。多くのライブラリには独自のメモリ管理がありません。通常、ユーティリティとAPIを提供します
  3. 実際にはオブジェクトであり、ポインターではないため、ラッパーとして実装されます。生のポインタと比較して、追加の時間/空間コストがあります。ライブラリのユーザーは、このようなオーバーヘッドを持ちたくない場合があります

編集:スマートポインターの使用は、完全に開発者の選択です。それはさまざまな要因に依存します。

  1. パフォーマンスが重要なシステムでは、オーバーヘッドを生成するスマートポインターを使用したくない場合があります。

  2. 後方互換性が必要なプロジェクトでは、C++ 11固有の機能を備えたスマートポインターを使用したくない場合があります。

Edit2通過が遅れているため、24時間の間にいくつかのダウン票のストリングがあります。以下は単なるアドオンの提案であり、答えではないのに、なぜ答えがダウン投票されるのか理解できません。
ただし、C++では常にオプションを開くことができます。 :)例:.

_template<typename T>
struct Pointer {
#ifdef <Cpp11>
  typedef std::unique_ptr<T> type;
#else
  typedef T* type;
#endif
};
_

そして、あなたのコードで次のように使用します:

_Pointer<int>::type p;
_

スマートポインターと生のポインターは異なると言う人のために、私はそれに同意します。上記のコードは単なるideaで、_#define_と交換可能なコードを書くことができますが、これはcompulsion;

たとえば、_T*_は明示的に削除する必要がありますが、スマートポインターは削除しません。それを処理するテンプレート化されたDestroy()を持つことができます。

_template<typename T>
void Destroy (T* p)
{
  delete p;
}
template<typename T>
void Destroy (std::unique_ptr<T> p)
{
  // do nothing
}
_

そしてそれを次のように使用します:

_Destroy(p);
_

同様に、生のポインターの場合は直接コピーでき、スマートポインターの場合は特別な操作を使用できます。

_Pointer<X>::type p = new X;
Pointer<X>::type p2(Assign(p));
_

Assign()は次のとおりです。

_template<typename T>
T* Assign (T *p)
{
  return p;
}
template<typename T>
... Assign (SmartPointer<T> &p)
{
  // use move sematics or whateve appropriate
}
_
40
iammilind

スマートポインターには2つの問題があります(C++ 11より前):

  • 標準ではないため、各ライブラリは独自に再発明する傾向があります(NIHシンドロームと依存関係の問題)
  • 潜在的なコスト

デフォルトスマートポインターは、コストがかからないという点で、unique_ptr。残念ながら、最近登場したばかりのC++ 11移動セマンティクスが必要です。他のすべてのスマートポインターにはコスト(shared_ptrintrusive_ptr)または理想的なセマンティクス(auto_ptr)。

角を曲がったC++ 11で、std::unique_ptr、それは最終的に終わったと思うように誘惑されるだろう...私はそれほど楽観的ではありません。

C++ 11のほとんどを実装しているのは、ごく最近のバージョンでのみ、少数の主要なコンパイラーのみです。 QTやBoostなどの主要なライブラリは、しばらくの間C++ 03との互換性を維持することを望んでいるため、新しくスマートなスマートポインターが広く採用されることはある程度ありません。

35
Matthieu M.

スマートポインターに近づかないでください。スマートポインターは、特にオブジェクトを渡す必要があるアプリケーションで使用できます。

ライブラリは、単に値を返すか、オブジェクトに入力する傾向があります。通常、多くの場所で使用する必要があるオブジェクトがないため、スマートポインターを使用する必要はありません(少なくともインターフェイスでは使用せず、内部で使用できます)。

私が取り組んでいるライブラリを例に取ると、数か月の開発の後、いくつかのクラス(すべてのクラスの3-5%)でのみポインターとスマートポインターを使用していることに気付きました。

参照による変数を渡すことは、ほとんどの場所で十分であり、nullになる可能性のあるオブジェクトがある場合は常にスマートポインターを使用し、使用するライブラリが強制する場合はrawポインターを使用しました。

編集(評判のためコメントできません):参照による変数の受け渡しは非常に柔軟です:オブジェクトを読み取り専用にしたい場合は、const参照を使用できます(いやらしいキャストができます)オブジェクトを記述できるようにするため)可能な限り最大限の保護を取得します(スマートポインターでも同じです)。しかし、オブジェクトを返すだけの方がずっといいことに同意します。

12
Robot Mess

QtはJavaになろうとして標準ライブラリの多くの部分を無意味に再発明しました。現在、実際には独自のスマートポインターを持っていると思いますが、一般的には、デザインの頂点ではありません。 wxWidgetsは、私の知る限り、使用可能なスマートポインターが作成されるずっと前に設計されました。

Boostについては、適切な場所でスマートポインターを使用することを完全に期待しています。もっと具体的にする必要があるかもしれません。

さらに、所有権を強制するスマートポインターが存在することを忘れないでください。 APIに所有権セマンティクスがない場合、スマートポインターを使用する理由

9
Puppy

良い質問。あなたが参照する特定の記事は知りませんが、時々似たようなものを読んでいます。私の疑いは、そのような記事の著者はC++スタイルのプログラミングに対する偏見を抱く傾向があるということです。ライターが必要な場合にのみC++でプログラムし、Javaなどにできるだけ早く戻るなど)すると、実際にはC++の考え方を共有しません。

同じライターの一部またはほとんどがガベージコレクションメモリマネージャーを好むと思われます。私はそうはしませんが、彼らとは違う考え方をします。

スマートポインターは優れていますが、参照カウントを保持する必要があります。参照カウントの保持は、実行時にコストを負担します。多くの場合、控えめなコストですが、それでもコストがかかります。特にポインタがデストラクタによって管理されている場合は、ベアポインタを使用してこれらのコストを節約することは問題ありません。

C++の優れた点の1つは、組み込みシステムプログラミングのサポートです。ベアポインタの使用はその一部です。

更新:コメンターは、C++の新しいunique_ptr(TR1以降で使用可能)は参照をカウントしません。また、コメンターは、私が考えているものとは異なる「スマートポインター」の定義を持っています。彼はその定義について正しいかもしれません。

更なる更新:以下のコメントスレッドが光っています。すべて読むことをお勧めします。

3
thb

他の種類のスマートポインターもあります。ネットワークレプリケーション(アクセスされたかどうかを検出し、サーバーなどに変更を送信するもの)、変更の履歴を保持、アクセスされたという事実をマークして、いつ調査できるようにするための特別なスマートポインターが必要な場合がありますデータをディスクなどに保存します。ポインターでそれを行うのが最善の解決策であるかどうかはわかりませんが、組み込みのスマートポインタータイプをライブラリで使用すると、ロックされて柔軟性が失われる可能性があります。

人々は、スマートポインターを超えて、あらゆる種類の異なるメモリ管理要件とソリューションを持つことができます。私は自分でメモリを管理したいと思うかもしれません。メモリプール内の物にスペースを割り当てて、実行時ではなく事前に割り当てることができます(ゲームに便利です)。ガベージコレクションされたC++の実装を使用している可能性があります(まだ存在していませんが、C++ 11はこれを可能にします)。あるいは、それらを悩ませることを心配するほど高度なことは何もしていないのかもしれません。初期化されていないオブジェクトなどを忘れないことを知っています。たぶん、私はポインタを松葉杖なしでメモリを管理する能力に自信があるだけです。

Cとの統合も別の問題です。

別の問題は、スマートポインターがSTLの一部であるということです。 C++は、STLなしで使用できるように設計されています。

2
David C. Bishop

それはあなたがどのドメインで働いているかにも依存します。私は生活のためにゲームエンジンを書いています。私たちはペストのようなブーストを避けます。コアエンジンでは、独自のバージョンのstlを作成しました(ea stlに似ています)。

フォームアプリケーションを作成する場合、スマートポインターの使用を検討できます。しかし、一度メモリ管理が第二の性質になると、メモリをきめ細かく制御できないため、静かに迷惑になります。

0
Ugly Davis