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


156

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

しかし、Qtのようなフレームワーク、wxWidgets、Boostのようなライブラリーは、スマートポインターをまったく使用していないかのように、返ったり期待したりしないことに気づきました。代わりに、生のポインタを返すか期待します。その理由はありますか?パブリックAPIを作成するとき、スマートポインターに近づかないでください。なぜですか。

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


22
名前を付けたばかりのライブラリはすべて、何年も前に開始されました。スマートポインターは、C ++ 11で本当に標準になっただけです。
chrisaycock 2012

22
スマートポインターには、オーバーヘッド(参照カウントなど)があります。これは、たとえば組み込み/リアルタイムシステムでは重要な場合があります。IMHO-スマートポインタは、怠惰なプログラマ向けです。また、多くのAPIは、共通点が最も低くなっています。入力すると、足の周りで炎がなめられます。
Ed Heal 2012

93
@EdHeal:足の周りで炎がなめるのを感じることができる理由は、あなたがあらゆる点で完全に間違っているからです。たとえば、どのようなオーバーヘッドがありunique_ptrますか?まったくありません。Qt / WxWidgetsは組み込みシステムまたはリアルタイムシステムをターゲットにしていますか?いいえ、デスクトップでのWindows / Mac / Unix向けです。スマートポインターは、それを正しく取得したいプログラマー向けです。
パピー

24
実際、携帯電話はJavaを実行しています。
R.マルティーニョフェルナンデス

12
スマートポインターはC ++ 11で本当に標準的なだけですか?何???これらのものは20年以上使用されています。
Kaz 2012

回答:


124

多くのライブラリが標準のスマートポインタが登場する前に作成されたという事実は別として、最大の理由はおそらく標準のC ++アプリケーションバイナリインターフェース(ABI)の欠如です。

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

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

要するに、ライブラリのビルド済みバージョンを配布する場合は、依存する標準のABIが必要です。Cにはありませんが、コンパイラベンダーは、特定のプラットフォームのCライブラリ間の相互運用性に優れており、事実上の標準があります。

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

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


17
std :: stringを渡すことさえ苦痛になる可能性があるので、私はあなたに同意します。これは、C ++を「ライブラリ用の素晴らしい言語」として多く語っています。
2012

8
つまり、プリビルドバージョンを配布する場合は、サポートするコンパイラごとに配布する必要があります。
josefx 2012

6
@josefx:はい、これは悲しいですが本当です。唯一の選択肢はCOMまたは生のCインターフェースです。C ++コミュニティがこの種の問題を心配するようになることを願っています。つまり、C ++が2年前の新しい言語ではないということです。
Robot Mess 2012

3
これは間違っているので、私は反対票を投じました。ABIの問題は、ほとんどの場合管理可能です。ユーザーフレンドリーではありませんが、ABIも乗り越えられません。
子犬

4
@NathanAdams:そのようなソフトウェアは間違いなく印象的で便利です。しかし、これはより深い問題の症状を扱います。存続期間と所有権のC ++セマンティクスは、貧困層と非存在層の中間にあります。言語で許可されていなければ、これらのヒープバグは発生しません。確かに、スマートポインターは万能薬ではありません。そもそも、C ++を使用することで生じた損失の一部を取り戻す試みです。
Jon Purdy 2013

40

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

  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;

スマートポインターとローポインターが異なると言う人にとっては、私はそれに同意します。上記のコードは単にと交換可能なコードを記述できるアイデアであり#define、これは強制ではありません。

たとえば、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
}

14
3. いくつかのスマートポインターには追加の時間/スペースコストがありますが、他にはないものもあります。これstd::auto_ptrは、長い間標準の一部でした(そして、std::auto_ptrオブジェクトを作成する関数の戻り型として、他の場所ではほとんど役に立たない)。C ++ 11 std::unique_ptrでは、単純なポインタを超える追加のコストはありません。
DavidRodríguez-dribeas

4
まさに...の出現unique_ptrと消滅には良い対称性がauto_ptrあります。C++ 03をターゲットとするコードは後者を使用する必要がありますが、C ++ 11をターゲットとするコードは前者を使用できます。スマートポインタがありません shared_ptr、多くの標準となし標準は、として拒否された標準への提案を含め、そこにあるmanaged_ptr
デビッド・ロドリゲス- dribeas

2
@iammilind、これらは興味深い点ですが、面白いことに、明らかに多くの人が推奨しているように、スマートポインターを使用すると、主要なライブラリと互換性のないコードが作成されます。もちろん、必要に応じてスマートポインターをラップ/アンラップすることもできますが、面倒で、一貫性のないコードが作成される可能性があります(スマートポインターを処理する場合としない場合があります)。
laurent 2012

7
スマートポインターに「追加の時間/スペースコスト」があるという説明は、誤解を招く可能性があります。unique_ptrランタイムコストが発生する以外のすべてのスマートポインターunique_ptrですが、これは最も一般的に使用されるものです。ので、あなたが提供するコードサンプルも、誤解を招くれるunique_ptrT*全く異なる概念です。あなたがそれらの両方を参照しているという事実は、それらをtype互いに交換できるという印象を与えます。
voidポインタ

12
あなたはそのようにtypedefすることはできません、これらのタイプは決して等価ではありません。このようなtypedefを書くことは問題を求めています。
Alex B

35

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

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

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

C ++ 11 std::unique_ptrが近づいてきて、を持ってくると、ついにそれが終わったと思うように誘惑されます...私はそれほど楽観的ではありません。

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


12

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

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

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

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

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


私はあなたと正確には反対しませんが、ほとんどの場合、変数参照の受け渡しを非難する学派が存在することを指摘しておきます。私はその学校に忠実であることを告白します。引数を変更しない関数を好みます。とにかく、私が知る限り、C ++の変数参照は、参照先のオブジェクトの誤処理を防ぐために何もしません。これは、スマートポインターが意図していることです。
2012

2
あなたはそのためのconstを持っています(私はコメントできるようです:D)。
Robot Mess 2012

9

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

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

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


19
Qtは、使用したいプラットフォームで機能の多くが十分に普及する前に作成されました。長い間スマートポインタがあり、それらを使用してほとんどすべてのQ *クラスのリソースを暗黙的に共有しています。
rubenvb

6
すべての GUIライブラリが不必要にホイールを再発明します。文字列でさえ、QtにはありQString、wxWidgetsにはありwxString、MFCには恐ろしい名前が付けられていCStringます。UTF-8 std::stringは99%のGUIタスクに十分ではありませんか?

10
@Inverse QStringはstd :: stringがなかったときに作成されました。
MrFox 2012

qtが作成された時期と、その時点で利用可能なスマートポインターを確認します。
Dainius

3

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

同じライターの一部またはほとんどがガベージコレクションのメモリマネージャーを好むのではないかと疑う人もいます。私はそうではありませんが、私は彼らとは違った考え方をしています。

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

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

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

更なる更新: 以下のコメントスレッドは明快です。それはすべて読むことをお勧めします。


2
まず、組み込みシステムプログラミングはすべてのプログラミングの非常に少数であり、まったく無関係です。C ++は汎用言語です。次に、shared_ptr参照カウントを保持します。参照カウントをまったく保持しない他の多くのスマートポインター型があります。最後に、言及されているライブラリは、余裕のあるリソースがたくさんあるプラットフォームをターゲットにしています。私が反対投票者だったというわけではありませんが、私が言っているのはあなたの投稿が間違っていることだけです。
パピー

2
@thb-私はあなたの感情に同意します。DeadMG-組み込みシステムなしで生きてみてください。はい-一部のスマートポインターにはオーバーヘッドがありませんが、一部にはあります。OPはライブラリについて言及しています。たとえば、Boostには組み込みシステムで使用されるパーツがありますが、スマートポインターは特定のアプリケーションには不適切な場合があります。
Ed Heal 2012

2
@EdHeal:組み込みシステムなしでは生きられない!=それらのためのプログラミングは、小さく、無関係で、少数派ではありません。スマートポインターは、リソースの有効期間を管理する必要があるすべての状況に適しています。
パピー

4
shared_ptrオーバーヘッドはありません。スレッドセーフな共有所有権セマンティクスが必要ない場合にのみオーバーヘッドが発生します。
R.マルティーニョフェルナンデス

1
いいえ、shared_ptrは、スレッドセーフな共有所有権のセマンティクスに必要な最小限のものよりも大きなオーバーヘッドがあります。具体的には、refcountを格納することのみを目的として、共有している実際のオブジェクトとは別にヒープブロックを割り当てます。intrusive_ptrはより効率的ですが、(shared_ptrと同様に)オブジェクトへのすべてのポインタがintrusive_ptrであると想定しています。アプリで行うように、カスタムの参照カウント共有ポインターを使用すると、trusive_ptrよりもオーバーヘッドが低くなり、少なくとも1つのスマートポインターがT *値を超えることが保証できる場合は常にT *を使用できます。
Qwertie 2012

2

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

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

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

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


もう1つの問題は、スマートポインターがSTLの一部であるということです。」そうではありません。
curiousguy

0

それはあなたがどのドメインで働いているかにも依存します。私は生活のためのゲームエンジンを書きます。私たちは疫病のようなブーストを避けます。ゲームではブーストのオーバーヘッドは許容できません。コアエンジンでは、独自のバージョンのstlを作成しました(ea stlとほぼ同じ)。

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


3
「ブーストのオーバーヘッド」などはありません。
curiousguy

4
shared_ptrでゲームエンジンの速度を著しく低下させたことはありません。彼らは生産とデバッグのプロセスをスピードアップしました。また、「ブーストのオーバーヘッド」とはどういう意味ですか?それはキャストするのにかなり大きな毛布です。
derpface 2013年

@curiousguy:これは、これらすべてのヘッダーとマクロ+テンプレートブードゥーのコンパイルオーバーヘッドです...
einpoklum
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.