2011-09-12 8 views
12

最近、管理ポインタを学習し、以下のシナリオに遭遇しました。スタックオブジェクトのC++ shared_ptr

私はゲームビュー用のモデル/コントローラクラスを実装しています。私の見解は、モデル内のものをレンダリングします。かなりストレートです。私の主な機能には、私はこのような3つのすべてのインスタンスを作成:

RenderModel m; 
m.AddItem(rect); // rect gets added just fine, it's an "entity" derivee 
RenderView v; 
v.SetModel(m); 

はビュークラスをレンダリングマイ非常に簡単です:

class RenderView 
{ 
public: 
explicit RenderView(); 
~RenderView(); 

void Update(); 

void SetModel(RenderModel& model); 

private: 
// disable 
RenderView(const RenderView& other); 
RenderView& operator=(const RenderView& other); 

// private members 
boost::scoped_ptr<RenderModel> _model; 
}; 

setviewコマンドの実装はかなり標準です:

void RenderView::SetModel(RenderModel& model) 
{ 
    _model.reset(&model); 
} 

これの鍵は、スマートポインタにモデルを保存するビューです。しかし、メインでは、モデルはスタックに割り当てられました。プログラムが終了すると、メモリは2回削除されます。意味あり。私の現在の理解では、(任意の種類の)smart_ptrに格納されるものは、スタックに割り当てられてはならないということがわかります。

上記の設定のすべての後、私の質問は簡単です:どのようにパラメータがスタックに割り当てられていないことを指示するのですか?唯一の解決策としてスマートポインタをパラメータとして受け入れていますか?

私はパラメータがスタックに割り当てられていなかったことを指示んか
// If I implemented SetModel this way: 
void RenderView::SetModel(const std::shared_ptr<RenderModel>& model) 
{ 
    _model.reset(&*model); 
} 

RenderModel m; 
RenderView v; 
std::shared_ptr<RenderModel> ptr(&m); // create a shared_ptr from a stack-object. 
v.SetModel(ptr); 

答えて

8

:それでも私は、私の見解クラスを使用して誰かのような間違った何かをすることができなかったことを確認することができませんでしたか?

はい、発信者にstd::shared_ptr<RenderModel>を提供する必要があります。発信者がstd::shared_ptrを誤って構成すると、それが発信者の問題であり、自分の問題ではありません。

あなたが特定のRenderModelの唯一の所有者であることをRenderViewを意図した場合、関数は取ることを検討std::unique_ptrまたはstd::auto_ptr代わりに、この方法では、関数呼び出し後に呼び出し元がオブジェクトの所有権を保持すべきではないことは明らかです。

RenderModelは、コピーし、そのコピーを作成し、そのコピーを使用して安価であれば別の方法として、:

_model.reset(new RenderModel(model)); 
+0

発信者がshared_ptrを誤って構成している場合は、このようなものを検出する方法がないと思いますか? 私はこのミスを再び犯すかもしれないと思うので、私は簡単に尋ねます。問題を再デバッグするのに何時間も費やしたくありません。私はそれが私の頭の中に燃え尽きるまで自分のモニターに付箋を残すことができました... – Short

+0

アドレスを比較するような、オブジェクトがスタックかヒープに存在するかどうかを検出するための既知の「トリック」があります。これらはすべて、未定義または実装に依存する動作でリレーします。私はそれがうまくいくかもしれないと思っているが、本当の解決策ではないと思う。 –

+3

いいえ、関数が 'std :: shared_ptr'を受け取った場合、そのポインタが有効なオブジェクトを指しているかどうかを判断する方法はありません。つまり、明示的に 'std :: shared_ptr'を取ると、(ローカル変数からの' std :: shared_ptr'を構成するコードは、最初の視点では間違って見えてしまい、避けるのは簡単です)。 –

3

あなたはおそらくより明確に自分のクラスのセマンティクスを定義する必要があります。 RenderViewをRenderModelの所有者にしたい場合は、それを独自に作成する必要があります(おそらく、コンストラクタでファクトリとともに使用するための識別子を取得する必要があります)。

オブジェクトの所有権を受け取るクラスがあり、このオブジェクトがヒープ上にある必要があることを明示的に定義していましたが、これは私の意見では、エラーが発生しやすくなりました。ヒープ上にあることを期待しているスマートポインタにスタックオブジェクトを与えることはできません(スマートポインタを消去するときにその上で削除を使用するため)。

+0

Qtは上記のモデルを使用しています。つまり、私はそこから考えを得ています。 1つのモデルに複数のビューをリンクすることができます。私は彼らの実装をより詳しく調べなければならないと思います。 – Short

2

あなたがしたいことを説明した方法は完全に間違っています。 MVPデザインパターンでは、ビューはモデルに直接アクセスすべきではありませんが、(プレゼンターの関数を呼び出すことによって)プレゼンターにコマンドを送信する必要があります。

とにかく、他のは、あなたの質問に答えている:あなたのモデルオブジェクトはこのように、ヒープに割り当てなければなりません:

std::shared_ptr<RenderModel> ptr(new RenderModel); 
RenderView v; 
v.SetModel(ptr); 

そうでない場合は、あなたのshared_ptrオブジェクトがスタックオブジェクトを削除しようとしています。

1

戻ってデザインを考えてください。最初のコードの匂いは、オブジェクトを参照してスマートポインタとして管理しようとしていることです。それはが間違っていますです。

RenderViewがリソースの管理を担当する場合は、参照を受け入れるべきではなく、(スマートな)ポインタを使用する必要があります。それが唯一の所有者の場合は、オーナーシップが希釈されている場合(可能であれば単独の所有者にすることを好む)、署名はstd::unique_ptr(またはコンパイラ+ libsがunique_ptrをサポートしていない場合はstd::auto_ptr)をとり、shared_ptrを使用してください。

RenderViewがリソースをまったく管理する必要がない他のシナリオもあります。この場合、参照によってモデルを取得し、寿命がRenderViewである間は変更できない場合は参照として保存できます。このシナリオでは、RenderViewがリソースの管理に責任を負いませんが、delete(スマートポインタを含む)を試みるべきではありません。

1
class ModelBase 
{ 
    //..... 
}; 

class RenderView 
{ 
    //.......... 
public: 
    template<typename ModelType> 
    shared_ptr<ModelType> CreateModel() { 
     ModelType* tmp=new ModelType(); 
     _model.reset(tmp); 
     return shared_ptr<ModelType>(_model,tmp); 
    } 

    shared_ptr<ModelBase> _model; 
    //...... 
}; 

モデルクラスのコンストラクタにパラメータがある場合、それはメソッドたcreateModel(にパラメータを追加することが可能です)とC++ 11件の完璧な転送テクニクスを使用します。

1

入力を正しく入力する必要があります。まず、入力変数を参照変数ではなくスマートポインタに変更します。次にスマートポインタに何もしない(NoDelete、例)スマートポインタを適切に渡してもらいます。

ハックされた方法は、メモリセグメントをチェックすることです。スタックは常にカーネル空間から成長します(私は32ビットで0xC0000000、64ビットでは0x7fff2507e800、以下のコードに基づいています)。スタック変数かどうかに関わらず、メモリの場所に基づいて推測することができます。人は移植性がないと言いますが、組み込みシステムにデプロイメントを導入する場合を除いて、そうではありません。

#include <iostream> 
#include <memory> 

using namespace std; 

class foo 
{ 
    public: 
    foo(shared_ptr<int> in) { 
     cerr << in.get() << endl; 
     cerr << var.use_count() << endl; 
     var = in; 
     cerr << var.use_count() << endl; 
    }; 

    shared_ptr<int> var; 
}; 

struct NoDelete { 
    void operator()(int* p) { 
     std::cout << "Not Deleting\n"; 
    }; 
}; 

int main() 
{ 
    int myval = 5; 

    shared_ptr<int> valptr(&myval, NoDelete()); 
    foo staticinst(valptr); 

    shared_ptr<int> dynptr(new int); 
    *dynptr = 5; 
    foo dynamicinst(dynptr); 
} 
0

要約すると、カスタムディテクタを定義します。スタックオブジェクトへのスマートポインタの場合は、あなたがこの場合「ヌルDELETER」(または「StackObjectDeleter」)で、カスタムデリータでスマートポインタを構築することができます

class StackObjectDeleter { 
public: 
    void operator() (void*) const {} 
}; 

std::shared_ptr<RenderModel> ptr(&m, StackObjectDeleter()); 

StackObjectDeleterはdefault_deleteように取って代わりますデリゲーターオブジェクトdefault_deleteは単にdelete(または[]を削除)を呼び出します。 StackObjectDeleterの場合、何も起こりません。

+0

神聖なネクロバットマン! – Short

関連する問題