2010-08-06 9 views
7

У меня есть класс, который использует механизм подсчета ссылок. Объекты этого класса в конечном итоге уничтожаются, вызывая delete this, когда счетчик ссылок падает до нуля. Мой вопрос: могу ли я использовать локальную переменную on-stack после delete this? Вот конкретный пример:Доступ к локальной переменной после «delete this»

class RefCountedClass 
{ 
public: 
    RefCountedClass(Mutex& m) : 
     mutex_(m) 
    {} 

    . 
    . 
    . 

private: 
    Mutex& mutex_; 

    void RemoveReference() 
    { 
     // As I understand, mutex_ will be destroyed after delete, 
     // but using m is all right because it is on-stack and 
     // references an external object. Am I right? 
     Mutex& m = mutex_; 
     m.Acquire(); 

     --recount_; 
     if (refcount <= 0) delete this; 

     m.Release(); 
    } 
}; 
+0

Наличие объектов подсчет ссылок на себя это плохая идея (Можно ли сказать, если это статическая продолжительность жизни (стек) или динамическая переменная продолжительности жизни (куча). Это то, как работает COM, и из опыта, накопленного сообществом C++, на котором мы перешли, как видно из boost :: shared_ptr, где счетчик ссылок а не часть объекта, посчитанного реферином. –

+0

@MartinYork Я согласен с вами. В общем случае я бы не советовал мне реализовывать такие подсчет ссылок. Это особый случай. К счастью, в моей реальной ситуации конструктор не является публичным, а создание защищено фабричным объектом. – FireAphis

ответ

7

Да, you may do this, до тех пор, как сама переменная член действительно только ссылка на внешний объект.

(Пожалуйста, простите предыдущий неправильный ответ, я был смущен о переменной mutex_.)

+0

+1 от меня. Я забыл, что было назначено локальной переменной. Мне нравится ваш ответ лучше, поэтому я удалил свою. – Manfred

0

Вообще единственное, что вы не можете позвонить после delete this это то, что делает ссылку на this. Сюда входит любой нестатический член класса или функция.

В вашем случае есть хороший шанс, что RemoveReference не будет работать. Это потому, что m указывает на mutex_, который больше не существует после удаления этого. Ваш лучший выбор вполне мог бы сделать mutex_ статическим.

Edit: Хотя mutex_ указует на внешний переменный, которая продолжает существовать после того, как класс удаляются, нет пуленепробиваемых гарантий, что компилятор не будет ссылаться на mutex_ после того, как класс удаляется, чтобы получить значение из внешний мьютекс. Есть хороший шанс, что все будет работать так, как ожидалось, но я не верю, что это может быть гарантировано.

+0

Нет, 'm' указывает не на' mutex_', а на то, что указывает 'mutex_': обратите внимание, что' mutex_' является ссылкой! –

+0

См. Мое редактирование. Я предполагаю, что ссылка рассматривается как указатель, но, будучи ссылкой, компилятор может кэшировать значение mutex_ и все работает так, как ожидалось. – doron

+3

Если компилятор делает то, что вы предполагаете, он может быть поврежден. Это законный C++. Положите другой способ: как эта ситуация отличается от 'int a = myIntegerMemberVariable_; удалить это; doSomethingWith (а); '?Разве не ясно, в этом случае, что любой компилятор, который «оптимизирует» это для 'doSomethingWith (this-> myIntegerMemberVariable _);' нарушен? –

0

Да в этом случае. Ваша переменная стека «m» указывает на внешний ресурс, который вы получаете в конструкторе

1

Да, вы можете, но почему бы не использовать атомный декремент вместо декремента счетчика под мьютексом? И вам на самом деле нужно защитить (от мьютекса) уничтожение объекта? Действительно, после того, как counter becames 0, только текущий поток может захватить объект.

Так, может быть, можно переписать код, как

 
    int tmp_count; 
    m.Acquire(); 
     tmp_count= --recount_; 
    m.Release(); 
    if (tmp_count <= 0) delete this; 

(или использовать атомарных декрементировать и проверить счетчик)

+1

** Опасный **! Это может быть дважды удалено, так как вы проверяете 'tmp_count <= 0'. Было бы безопасно протестировать вместо '== 0'. Но этот пример прекрасно иллюстрирует, почему одновременный код так трудно получить правильно. –

+0

Это опасно, если программа содержит еще одну ошибку (recount_ не может быть <0 в противном случае); но в целом я согласен с тем, что == более приемлемо в этом случае. +1 о параллельном коде. Время тратить, чтобы проанализировать и проверить, что такой код может быть в 10,20..50 раз больше времени, потраченного на его запись. – user396672

Смежные вопросы