Предотвратить временное продление его жизни? - PullRequest
8 голосов
/ 28 сентября 2011

Это может быть невозможно, но мне было интересно, можно ли было удержать временное от вечного прошлого его первоначальное выражение. У меня есть цепочка объектов, которые указывают на родительские объекты, и функция-член, которая создаст дочерний объект, упрощенный пример здесь

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"));

В порядке.

Это будет работать нормально, пока ни один из временных времен не выживет после исходного выражения, но если я свяжу один из последних временных значений с константной ссылкой, его родители не выживут, и я получу сегфо. Я могу скрыть конструктор копирования и оператор присваивания person, но есть ли способ предотвратить возникновение такой ошибки? Я хотел бы избежать динамического распределения, если это возможно.

Ответы [ 2 ]

3 голосов
/ 28 сентября 2011

Похоже, вы создаете здесь структуру данных, в которой дети имеют указатели на своих родителей. Использование временных средств в этом случае гарантирует вам горе. Для того чтобы сделать это безопасным, вам нужно будет динамически распределять и, возможно, использовать какой-то счетчик ссылок.

Рассматривали ли вы использование boost::shared_ptr? Это реализация класса интеллектуальных указателей с подсчетом ссылок. Используя shared_ptr и, возможно, некоторые фабричные методы, вы сможете получить желаемый эффект и уменьшить трудность динамического выделения памяти. Я попробовал это, и это похоже на работу. Как только код выходит из области видимости, все объекты уничтожаются, потому что не осталось ссылок на shared_ptrs.

Edit: В ответ на комментарий 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 голосов
/ 28 сентября 2011

Вам нужно будет сделать что-то вроде этого:

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

Таким образом, родительское «а» не выходит из области видимости, пока вы по-настоящему не покончили с ним (после вызова «use»).

Проблема с исходным кодом заключается в том, что родительский элемент «Анна» должен оставаться достаточно долго, чтобы вызвать «ребенка», и родительский элемент можно отбросить перед выполнением вызова функции. Сделав родительскую переменную с областью видимости, вы можете контролировать, когда она будет уничтожена.

Это выглядит опасно для меня? Да. Поэтому я бы посоветовал взглянуть на ответ m-sharp о динамическом размещении. Но если вам нужен метод, который не требует подсчета ссылок и т. Д., То вы можете сделать это следующим образом ...

...