shared_ptr удаляет объект - PullRequest
       6

shared_ptr удаляет объект

0 голосов
/ 10 сентября 2011
void ClassName::LocalMethod( )
{
    boost::shared_ptr<ClassName> classNamePtr( this );

    //some operation with classNamePtr
    return;
}

Здесь объект освобождается, когда он возвращается из LocalMethod (), так как classNamePtr находится вне области видимости. Разве shared_ptr не достаточно умен, чтобы знать, что объект ClassName все еще находится в области видимости, и не удалять его?

Ответы [ 2 ]

4 голосов
/ 10 сентября 2011

Что значит создать shared_ptr для объекта?Это означает, что владелец shared_ptr теперь принимает на себя владение объектом.Право собственности означает, что объект будет удален, когда он этого пожелает.Когда владелец shared_ptr уничтожает его shared_ptr, это может привести к потенциальному уничтожению объекта, при условии, что для этого объекта нет других shared_ptr s.

Когда shared_ptrчлен класса, это означает, что время жизни объекта, на которое указывает shared_ptr, равно по крайней мере , пока объект, членом которого является shared_ptr.Когда shared_ptr находится в стеке, это означает, что время жизни объекта, на который указывает shared_ptr, будет, по крайней мере, столько же, сколько и область, в которой он был создан. Как только объект падает со стека, он можетбыть удаленным.

только раз, когда вы должны взять указатель и заключить его в shared_ptr, это когда вы выделяете объект изначально .Зачем?Потому что объект не знает, находится ли он в shared_ptr или нет.Это не может знать.Это означает, что человек, создавший оригинал shared_ptr, теперь обязан передать его другим людям, которым необходимо разделить права собственности на эту память.Единственный способ совместного владения - через конструктор копирования из shared_ptr.Например:

shared_ptr<int> p1 = new int(12);
shared_ptr<int> p2 = p1.get();
shared_ptr<int> p3 = p1;

Конструктор копирования shared_ptr создает общее владение между p1 и p3.Обратите внимание, что p2 не делит владельца с p1.Они оба думают, что владеют одной и той же памятью, но это не то же самое, что делиться ею.Потому что они оба думают, что они владеют уникальным владельцем.

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

В этот момент p1 указывает на удаленную память.Когда p1 уничтожено, оно думает, что оно является единственным держателем целого числа, поэтому оно уничтожит его.Это плохо , так как оно уже было уничтожено.

Ваша проблема заключается в следующем.Вы находитесь в экземпляре класса.И вам нужно вызвать некоторые из ваших функций, которые занимают shared_ptr.Но все, что у вас есть, это this, который является обычным указателем.Что вы делаете?

Вы получите несколько примеров, которые предлагают enable_shared_from_this.Но рассмотрим более актуальный вопрос: «почему эти функции принимают shared_ptr в качестве аргумента?»

Тип указателя, который принимает функция, указывает на то, что эта функция делает со своим аргументом.Если функция принимает shared_ptr, это означает, что ей нужно иметь указатель.Это должно взять общее владение памятью.Итак, посмотрите на ваш код и спросите, действительно ли эти функции нуждаются , чтобы стать владельцем памяти.Хранят ли они shared_ptr где-то в течение длительного времени (то есть: в объекте), или они просто используют их во время вызова функции?

Если это последнее, то функции должны приниматьголый указатель, а не shared_ptr.Таким образом, они не могут претендовать на право собственности.В этом случае ваш интерфейс самодокументируется: тип указателя объясняет владение.

Однако возможно, что вы вызываете функции, которые действительно должны принять совместное владение.Тогда вам нужно использовать enable_shared_from_this.Во-первых, ваш класс должен быть получен из enable_shared_from_this.Затем в функции:

void ClassName::LocalMethod()
{
    boost::shared_ptr<ClassName> classNamePtr(shared_from_this());

    //some operation with classNamePtr
    return;
}

Обратите внимание, что здесь есть стоимость.enable_shared_from_this ставит boost::weak_ptr в классе.Но нет виртуальных накладных расходов или чего-то подобного;это не делает класс виртуальным.enable_shared_from_this - это шаблон, поэтому вы должны объявить его следующим образом:

class ClassName : public boost::enable_shared_from_this<ClassName>
3 голосов
/ 10 сентября 2011

Разве shared_ptr не достаточно умен, чтобы знать, что объект ClassName все еще в объеме и не удалить его?

Это не так, как shared_ptr работает. Когда вы передаете указатель при создании shared_ptr, shared_ptr примет владение объекта (в данном случае *this). Другими словами, shared_ptr предполагает полный контроль над временем жизни pointee в силу того факта, что shared_ptr теперь владеет им. Из-за этого последний shared_ptr, владеющий pointee, удалит его.

Если не будет копий classNamePtr за пределами ClassName::LocalMethod(), , вы можете передать средство удаления, которое ничего не делает при построении classNamePtr. Вот пример пользовательского средства удаления, используемого для предотвращения shared_ptr удаления своего указателя . Адаптируем пример к вашей ситуации:

struct null_deleter // Does nothing
{
    void operator()(void const*) const {}
};

void ClassName::LocalMethod() 
{
    // Construct a shared_ptr to this, but make it so that it doesn't
    // delete the pointee.
    boost::shared_ptr<ClassName> classNamePtr(this, null_deleter()); 
    // Some operation with classNamePtr 

    // The only shared_ptr here will go away as the stack unwinds,
    // but because of the null deleter it won't delete this.
    return; 
}

Вы также можете использовать enable_shared_from_this, чтобы получить shared_ptr из this. Обратите внимание, что функция-член shared_from_this() работает только в том случае, если существующий shared_ptr уже указывает на this.

class ClassName : public enable_shared_from_this<ClassName> 
{ 
public: 
    void LocalMethod()
    { 
        boost::shared_ptr<ClassName> classNamePtr = shared_from_this(); 
    } 
} 

// ...

// This must have been declared somewhere...
shared_ptr<ClassName> p(new ClassName);
// before you call this:
p->LocalMethod();

Это более подходящий, "официальный" метод, и он гораздо менее хакерский, чем метод нулевого удаления.

Возможно также, что вам вообще не нужно создавать shared_ptr. Что входит в комментируемый раздел //some operation with classNamePtr? Возможно, есть даже лучший способ, чем первые два.

...