12

私はさまざまなスマートポインターの実装を評価してきましたが(うわー、そこにはたくさんあります)、それらのほとんどは2つの大まかな分類に分類できるようです。

1)このカテゴリは、参照されるオブジェクトの継承を使用して、参照カウントがあり、通常はup()およびdown()(またはそれらに相当するもの)が実装されているようにします。IE、スマートポインターを使用するには、ポイントしているオブジェクトは、ref実装が提供するクラスから継承する必要があります。

2)このカテゴリは、参照カウントを保持するためにセカンダリオブジェクトを使用します。たとえば、スマートポインターをオブジェクトに直接向ける代わりに、実際にはこのメタデータオブジェクトを指します...参照カウントとup()およびdown()の実装を持っている人(そして通常、ポインターのメカニズムを提供する人)スマートポインタが演算子->()を適切に実装できるように、ポイントされている実際のオブジェクトを取得します。

これで、1には、カウントを参照するすべてのオブジェクトが共通の祖先から継承するように強制されるという欠点があります。これは、ソースコードを制御できないカウントオブジェクトを参照するためにこれを使用できないことを意味します。に。

2には、カウントが別のオブジェクトに格納されているため、既存の参照カウントオブジェクトへのポインタが参照に変換されているという状況が発生した場合、おそらくバグが発生するという問題があります(つまり、カウントが実際のオブジェクトでは、新しい参照がカウントを取得する方法はありません... ref to refコピーの構築または割り当ては、カウントオブジェクトを共有できるため問題ありませんが、ポインタから変換する必要がある場合は、完全にホースで固定されています)..。

さて、私が理解しているように、boost :: shared_pointerはメカニズム2、またはそのようなものを使用しています...とはいえ、私は自分の決心を完全に決めることはできません。私はこれまで本番コードでメカニズム1のみを使用しました...誰かが両方のスタイルの経験がありますか?または、おそらくこれらの両方よりも優れた別の方法がありますか?

4

9 に答える 9

9

ユビキタスな Boost の回答に別の見方をするために (多くの用途では正しい回答ですが)、Lokiのスマート ポインターの実装を見てください。設計哲学に関する談話として、Loki の最初の作成者はModern C++ Designという本を書きました。

于 2009-02-02T17:13:21.507 に答える
7

私はboost::shared_ptrを数年間使用していますが、欠点については正しいですが(ポインターを介した割り当てはできません)、ポインター関連のバグが大量にあるため、間違いなく価値があると思います。 。

In my homebrew game engine I've replaced normal pointers with shared_ptr as much as possible. The performance hit this causes is actually not so bad if you are calling most functions by reference so that the compiler does not have to create too many temporary shared_ptr instances.

于 2009-02-02T16:44:01.740 に答える
3

Boostには(ソリューション1のような)侵入型ポインターもあり、何からも継承する必要はありません。参照カウントを格納し、適切なメンバー関数を提供するには、クラスへのポインターを変更する必要があります。これは、メモリ効率が重要であり、使用される共有ポインタごとに別のオブジェクトのオーバーヘッドが必要ない場合に使用しました。

例:

class Event {
public:
typedef boost::intrusive_ptr<Event> Ptr;
void addRef();
unsigned release();
\\ ...
private:
unsigned fRefCount;
};

inline void Event::addRef()
{
  fRefCount++;
}
inline unsigned Event::release(){
    fRefCount--;
    return fRefCount;
}

inline void intrusive_ptr_add_ref(Event* e)
{
  e->addRef();
}

inline void intrusive_ptr_release(Event* e)
{
  if (e->release() == 0)
  delete e;
}

Ptr typedefを使用すると、クライアントコードを変更せずに、boost ::shared_ptr<>とboost::intrusive_ptr<>を簡単に切り替えることができます。

于 2009-02-02T16:55:14.307 に答える
3

あなたが標準ライブラリにあるものに固執するならば、あなたは大丈夫でしょう。
指定したもの以外にもいくつかありますが。

  • 共有:所有権が複数のオブジェクト間で共有される場合
  • 所有:1つのオブジェクトがオブジェクトを所有しているが、転送が許可されている場合。
  • 移動不可:1つのオブジェクトがオブジェクトを所有していて、転送できない場合。

標準ライブラリには次のものがあります。

  • std :: auto_ptr

Boostには、tr1(標準の次のバージョン)によって適応されたものよりもいくつかあります。

  • std :: tr1 :: shared_ptr
  • std :: tr1 :: weak_ptr

そして、うまくいけばtr2にするために、まだブーストしているもの(とにかく比較的必要です)。

  • boost :: scoped_ptr
  • boost :: scoped_array
  • boost :: shared_array
  • boost :: intrusive_ptr

参照: スマートポインター:または、誰があなたの赤ちゃんを所有していますか?

于 2009-02-02T17:03:47.423 に答える
2

この質問は、「最適なソート アルゴリズムはどれですか?」という質問に似ているように思えます。答えはひとつではありません。状況によって異なります。

私自身の目的のために、タイプ 1 を使用しています。TR1 ライブラリにはアクセスできません。ポインターを共有する必要があるすべてのクラスを完全に制御できます。タイプ 1 の追加メモリと時間効率はかなりわずかかもしれませんが、メモリの使用と速度は私のコードにとって大きな問題なので、タイプ 1 はスラムダンクでした。

一方、TR1 を使用できる人にとっては、タイプ 2 の std::tr1::shared_ptr クラスが賢明なデフォルトの選択であり、使用しない差し迫った理由がない場合はいつでも使用されると思います。

于 2009-02-02T17:13:19.963 に答える
1

2の問題は回避できます。Boost はこれと同じ理由で boost::shared_from_this を提供します。実際には、それは大きな問題ではありません。

しかし、彼らがあなたのオプション#2を採用した理由は、それがすべての場合に使用できるからです. 継承に依存することは常に選択肢とは限りません。そうすると、コードの半分を使用できないスマート ポインターが残ります。

あらゆる状況で使用できるという理由だけで、#2 が最適であると言わざるを得ません。

于 2009-02-02T16:53:28.960 に答える
1

私たちのプロジェクトでは、スマート ポインターを広く使用しています。最初は、どのポインターを使用するかについて不確実性があったため、主な作成者の 1 人は自分のモジュールで侵入ポインターを選択し、もう 1 人は非侵入バージョンを選択しました。

一般に、2 つのポインター型の違いは重要ではありません。唯一の例外は、初期バージョンの非侵入型ポインターが未加工のポインターから暗黙的に変換されたことです。これは、ポインターが正しく使用されていない場合、メモリの問題を簡単に引き起こす可能性があります。

void doSomething (NIPtr<int> const &);

void foo () {
  NIPtr<int> i = new int;
  int & j = *i;
  doSomething (&j);          // Ooops - owned by two pointers! :(
}

少し前に、一部のリファクタリングによってコードの一部がマージされたため、使用するポインターの型を選択する必要がありました。非侵入型ポインターには、変換コンストラクターが明示的に宣言されているため、必要なコード変更の量を節約するために侵入型ポインターを使用することが決定されました。

非常に驚いたことに、私たちが気づいたことの 1 つは、侵入ポインターを使用することでパフォーマンスがすぐに向上したことです。これについてはあまり調査せず、違いは count オブジェクトを維持するためのコストであると仮定しました。非侵入型共有ポインタの他の実装により、この問題が解決されている可能性があります。

于 2009-02-02T18:16:58.743 に答える
1

あなたが話しているのは、侵入型および非侵入型のスマートポインターです。ブーストには両方があります。boost::intrusive_ptr参照カウントを変更する必要があるたびに、オブジェクトの参照カウントを増減する関数を呼び出します。メンバー関数を呼び出すのではなく、フリー関数を呼び出します。そのため、型の定義を変更する必要なく、オブジェクトを管理できます。そして、あなたが言うように、boost::shared_ptr邪魔にならない、あなたのカテゴリー2.

intrusive_ptr: Making shared_ptr not use delete を説明する回答があります。つまり、すでに参照カウントされているオブジェクトがある場合、または intrusive_ptr によって所有されるために既に参照されているオブジェクトが必要な場合 (説明したように) に使用します。

于 2009-02-02T19:06:30.017 に答える