2009-12-22 3 views
4

Обзор довольно старый проект я нашел следующий любопытный фрагмент кода (только соответствующий код извлеченный):Что может означать эта любопытная комбинация «while» и «delete»?

class CCuriousClass { 
    ~CCuriousClass(); 
    CSomeType* object; 
}; 

CCuriousClass::~CCuriousClass() 
{ 
    while(object != NULL) { 
     delete object; 
    } 
} 

ли я что-нибудь под контролем, или это простой путь к неопределенному поведению?

То, что я вижу в том, что если object является нулевым указателем в точке CCuriousClass::~CCuriousClass() называют все будет в порядке - никаких действий не принимается - но если object не нулевое значение, это будет бесконечный цикл с неопределенным поведением внутри.

Является ли это скорее всего ошибкой или какой-то умной конструкцией, которую я не понимаю?

+0

Возможно, что-то происходит с этим объектом в другом потоке? – Blindy

+1

Я не знаю, но вы можете перегрузить оператор «delete» объекта? –

+1

@Mike, вы можете, но объект деструктор все равно будет вызван. –

ответ

6

Поскольку ваш вопрос, похоже, подразумевает «Что мог бы кто-то иметь в виду?» а не «Почему это фантастическая идея?» Я предлагаю следующее:

class CSomeType { 
    CCuriousClass* m_plistentry; 
    CSomeType* m_pnext; 

    ~CSomeType() { 
     m_plistentry->object = m_pnext; 
    } 
}; 

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

Теперь это явно код из сумасшедшего города.

+0

Он не может изменить указатель, удаляемый в исходном коде. – 2009-12-22 12:46:28

+0

@ Неужели мне любопытно, почему ты не можешь это сделать? –

+0

@ Нейл, да им тоже интересно. Это похоже на действительное объяснение мне. –

13

Это похоже на ошибку.

0

Код кажется ошибочным. Убедитесь, что деструктор не должен выбрасывать какие-либо исключения, иначе он может завершить программу, и это должно быть так.

CCuriousClass::~CCuriousClass() 
{ 
    try 
    {  
      if(object != NULL) 
      { 
       delete object; 
       object = NULL; 
      }  
    } 
    catch(...) 
    { } 
} 
3

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

1

Кажется, что ошибка, если деструктор CSomeType не может каким-либо образом изменить этот объект.

1

не только это, вероятно, ошибка, но это абсолютно бессмысленная проверка, так как удаление нулевого указателя в порядке. заменить на

CCuriousClass::~CCuriousClass() 
{  
    delete object;  
} 

или еще лучше использовать интеллектуальный указатель и полностью избавиться от деструктора.

9

Возможно, какой-то сумасшедший реализовал CSomeType с обратной ссылкой на свой собственный CCuriousClass, и его деструктор иногда создает замену. Что-то вроде этого:

class CSomeType 
{ 
public: 
    explicit CSomeType(CCuriousClass &parent) : parent(parent) {} 
    ~CSomeType() 
    { 
     parent.object = respawn() ? new CSomeType(parent) : 0; 
    } 
private: 
    CCuriousClass &parent; 
}; 

Я не предлагаю, чтобы кто-нибудь когда-либо писал такую ​​искривленную логику. Вероятно, он все еще дает неопределенное поведение, так как я считаю, что delete разрешено изменять указатель. Но это может объяснить, почему кто-то может подумать, что данный код может быть действительным.

С другой стороны, это, вероятно, просто ошибка, вызванная неправильным пониманием того, как работает delete.

+4

Это красиво! : p Теперь я собираюсь написать все мои деструкторы. – jalf

+0

respawn(), конечно, должен возвращать случайный бит для истинного зла –

+1

'delete' не имеет права изменять свой операнд (поскольку он даже не должен быть lvalue - считать' delete new int'), поэтому нет U.B. Вот. Просто явное зло. –

2

Это поведение возможно, если экземпляр CSomeType знает адрес, в котором хранится указатель на себя (член CSomeType ** в CSomeType), чтобы он мог сбросить его при удалении. Я понятия не имею, зачем это нужно.

пример:

struct self_know{ 
    self_know** pptr; 
    int cnt; 

    static self_know* create(self_know **_pptr){ 
     *_pptr = ::new self_know; 
     (*_pptr)->cnt = 10; 
     (*_pptr)->pptr = _pptr; 
     return *_pptr; 
    } 

    void operator delete(void*it){ 
     self_know *s = (self_know*)it; 
     if(--s->cnt<0){ 
     *(s->pptr)=0; 
     ::delete s; 
     } 

    } 
}; 

#include<iostream> 
main(){ 
    self_know *p = 0; 
    self_know::create(&p); 
    while(p != 0){ 
    std::cout << p->cnt << std::endl; 
    delete p; 
    } 
} 
1

Заканчивать определение класса CSomeType. Может быть перегрузка функции «! =». В противном случае это явно ошибка.

2

Еще одна возможная теория заключается в том, что кто-то играл противный трюк с препроцессором. Скажем:

struct delete_and_null { 
    template<class T> 
    delete_and_null& operator, (T*& p) { 
     delete p; 
     p = 0; 
     return *this; 
    } 
} delete_and_null; 

#define delete delete_and_null, 

Это не объясняет необходимость в цикле, но, по крайней мере, это позволит избежать U.B. и заканчивается в конце концов.

0

Я подозреваю, что это от сумасшедшего программиста на C, который не понимает деструкторов и/или оператора. Либо это, либо кто-то, кто неуклюже ошибся «минусом» вместо «если» и не знал, что delete уже проверяет значение null.

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

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