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 参照にバインドすると、その親が存続せず、セグメンテーション違反が発生します。のコピー コンストラクタと代入演算子を非表示にすることはpersonできますが、この種のエラーの発生を防ぐ方法はありますか? 可能であれば、動的割り当ては避けたいと思います。

4

2 に答える 2

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;
}
于 2011-09-27T20:23:58.880 に答える
0

次のようにする必要があります。

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

そうすれば、親の "a" は、("use" を呼び出した後) 本当に使い終わるまでスコープから外れません。

元のコードの問題は、親 "Anna" が "baby" を呼び出すのに十分な時間存在するだけでよく、関数呼び出しを行う前に親を破棄できることです。親をスコープ付きの変数にすることで、破棄するタイミングを制御できます。

これは私には危険に見えますか?はい。したがって、動的割り当てに関する m-sharp の回答をご覧になることをお勧めします。しかし、参照カウントなどを必要としないメソッドが必要な場合は、この方法で実行できます...

于 2011-09-27T20:29:40.483 に答える