2011-09-27 11 views
8

これは不可能かもしれませんが、一時的なものを元の表現よりも永続的に保つことが可能かどうかは疑問でした。私はポイントの親オブジェクトへのオブジェクトのチェーンを持っており、子オブジェクトを作成するメンバ関数、簡略化した例は、ここに一時的に一時的な存続期間が延長されないようにしますか?

class person{ 
    string name; 
    person * mommy; 
public: 
    person(const string & nam, person * m = 0) : name(nam), mommy(m) {} 
    person baby(const string & nam){ 
     return person(nam, this); 
    } 
    void talk() const{ 
     if (mommy) mommy->talk(); 
     cout << name << endl; 
    } 
}; 

int main(){ 
    person("Ann").baby("Susan").baby("Wendy").talk();  // fine 

    const person & babygirl = person("Julie").baby("Laura"); // not fine 

    babygirl.talk(); // segfault 
    return 0; 
} 

である私はpersonを使用する方法は、関数に渡すことで、このようなもの:

void use(const person & p) { 
    p.talk(); 
} 
use(person("Anna").baby("Lisa")); 

です。

一時的なものが元の式を過ぎても存続しない限り、これはうまく動作しますが、最終的な一時的なものの1つをconst参照にバインドすると、その親は生き残らないので、segfaultを取得します。私はpersonのコピーコンストラクタと代入演算子を隠すことができますが、この種のエラーが発生するのを防ぐことができる方法はありますか?可能であれば動的割り当てを避けたいと思います。

+0

@Konrad:Ironic; - ] – ildjarn

+1

このコードは同じように「うまくいかない」 'const int&i = std :: vector (1)[0];'を書くことは、 "うまくいかない"ということです。 'vector'はあなたがそれを書くことを止めず、する必要もありません。ここでの重要な点は、ママを破壊すると赤ちゃんが使えなくなるため、赤ちゃんはママの一部だということです。それがデザインに間違っているのですが、直観に反します。適切ではない孤児のようなものが存在しないようにすることでそれにパッチを当てようとしていますが、孤児が行動をより良く定義するべきかどうか、あるいは明らかに悪いものを作成するべきかを考慮する必要があります。 –

+0

Steveとの約束:これはちょうど悪いデザインの表示です。裸のポインタの外観は、何かが離れていることを明らかにしていたはずです。 –

答えて

3

ここでは、子供が親にポインタを持っているところにデータ構造を作成しているようです。一時的な使用はこの場合あなたに悲しみを引き起こすことが保証されています。これを安全にするためには、何らかの種類の参照カウントを動的に割り当てる必要があります。

boost::shared_ptrとお考えですか?これは、リファレンスカウントスマートポインタクラスの実装です。 shared_ptrとおそらくいくつかのファクトリメソッドを使用すると、必要な効果を得ることができ、動的メモリ割り当ての苦痛を軽減することができます。私はそれを試して、それは動作するようです。コードがスコープを終了すると、shared_ptrsに残っている参照がないため、オブジェクトはすべて破棄されます。

編集:zounds'コメントに応えて ルートオブジェクトは、データ構造の寿命を制御するように、私は例を変更しました。

#include <iostream> 
#include <string> 
#include <vector> 
#include <boost\shared_ptr.hpp> 
#include <boost\weak_ptr.hpp> 

using boost::shared_ptr; 
using boost::weak_ptr; 

using std::string; 
using std::cout; 
using std::endl; 
using std::vector; 

class person; 
typedef shared_ptr<person> Person; 
typedef weak_ptr<person> PersonWk; 

class person {  
    PersonWk pThis; 
    friend Person makePerson(const string & nam, Person m = Person()); 

    string name; 
    PersonWk mommy; // children should not affect parent lifetime, so store weak ptr 
    vector<Person> children; // parents affect children lifetime so store child shared ptrs 

    // make constructor private so that you can only make a person using factory method 
    person(const string & nam, Person m) : name(nam), mommy(m) 
    { 
     // for demo purposes 
     printf("creating %s\n", nam.c_str()); 
     ++personCount; 
    } 

    // undefined copy constructor and assignment operators 
    person(const person&); 
    person& operator=(const person&); 

public: 
    // for demo purposes 
    static int personCount; 

    ~person() 
    { 
     // for demo purposes 
     printf("destroying %s\n", name.c_str()); 
     --personCount; 
    } 

    Person baby(const string & nam){   
     Person child = makePerson(nam, Person(pThis)); 
     children.push_back(child); 
     return child; 
    } 

    void talk() const{ 
     if (Person mom = mommy.lock()) 
      mom->talk(); 
     cout << name << endl; 
    } 
}; 

int person::personCount = 0; 

// factory method to make a person 
Person makePerson(const string & name, Person m) { 
    Person p = Person(new person(name, m)); 
    p->pThis = p; // stash weak_ptr so I can use it to make a shared_ptr from "this" in the baby method 
    return p; 
} 

void use(const Person p) { 
    printf("In method use...\n"); 
    p->talk(); 
} 

int _tmain(int argc, _TCHAR* argv[]) 
{ 
    printf("personCount=%d\n", person::personCount); 
    { 
     Person ann = makePerson("Ann"); 

     // ann has baby and grandbaby, pass grandbaby to use method 
     use(ann->baby("Susan")->baby("Wendy")); 

     ann.reset(); // remove reference from root object. Destruction ensues... 
    } 
    printf("personCount=%d\n", person::personCount); 
    return 0; 
} 
+0

答えをありがとう。共有ptrの私にとっての問題は、ルートオブジェクトに特定の破壊ポイントが必要な場合があり、どこかに格納されたポインタがあるかもしれないことを心配したくありません。私は一時関数が関数呼び出しの間だけ存在する必要があり、それからそれらは死ぬことができます。子供が親に影響を与えることが許されていない子 - >親構造を使用しています(誰もが使用できる通常の公開インタフェースを除いて) – zounds

+0

既知の場所にルートオブジェクトを格納する方法があります。次に、各親にshared_ptrsを子に保たせ、子はweak_ptrsを親に保つことができます。次に、ルートオブジェクトをNULLに設定すると(その結果、すべての参照が削除されます)、ツリーは上から下に破棄されます。 –

+0

私の前のコメントでアプローチを使用する修正されたコードサンプル。 –

0

あなたはこのような何かをする必要があります:

void use(const person & p) { 
    p.talk(); 
} 
person a("Anna"); 
use(a.baby("Lisa")); 

あなたが本当に後に(それで終わっまで、親は「」スコープの外に出ていないその方法「使用」を呼び出す)。

元のコードの問題点は、親「Anna」が「baby」を呼び出すのに十分長く滞在すればよく、関数呼び出しを行う前に親を破棄できることです。スコープで親を変数にすることで、いつ破壊するのかを制御できます。

これは私にとって危険ですか?はい。だから私はダイナミックアロケーションのmシャープの答えを見てみたいと思う。しかし、参照カウントなどを必要としないメソッドが必要な場合は、この方法で行うことができます。

関連する問題