Возможный дубликат:
Безопасно ли удалять пустой указатель?
Скажи у меня есть new
распределение в класс под названием MyClass
и распределение так же просто, как:
MyClass *myClassPtr = new MyClass();
И я храню ссылку на список void*
где я просто говорю
myListOfPointers.add(static_cast<void*>(myClassPtr)); // this has to be void*
И позже я освобождаю память, вместо того чтобы делать:
delete myClassPtr
Я использую:
delete MyListOfPointer.get(0)
(Скажем, ссылка myClassPtr на нулевой индекс.) Также обратите внимание, что она должна быть void*
так как этот список может хранить различные типы указателей, поэтому я не знаю тип указателя, который я удаляю:
Поэтому я не могу сделать что-то вроде:
delete static_cast<MyClass*>(MyListOfPointer.get(0))
Собирается ли таким образом освободить правильный объем памяти? (sizeof(MyClass)
)?
Замечания:
Я не ищу ответа, указывающего на умные указатели.
Удаление через void*
приводит к неопределенному поведению, так что вам ничего не гарантировано.
5.3.5 Удалить [expr.delete]
1 Оператор delete-expression уничтожает наиболее производный объект (1.8) или массив, созданный новым выражением.
[…] Операнд должен иметь указатель на тип объекта или тип класса, имеющий одну неявную функцию преобразования (12.3.2) в указатель на тип объекта. Результат имеет тип void.7878) Это означает, что объект нельзя удалить с помощью указателя типа void *, поскольку void не является типом объекта.
Акцент мой.
Поэтому, даже если вы сказали не говорить этого, ответ состоит в том, чтобы создать некую форму умного указателя. Необходимо было бы использовать стирание типа, чтобы скрыть тип внешне (позволяя разнородный список), но внутренне отследить тип, который он был задан, и то, как его удалить. Нечто подобное boost::any
,
void
Указатель не имеет информации о типе. Если MyClass
есть деструктор, он не будет называться. Компилятор должен знать, что он удаляет, чтобы он мог генерировать соответствующий код. Если все ваши указатели в списке имеют одинаковый тип, то вы должны хранить этот тип в списке, а не как void
, Если указатели имеют разные типы, но являются производными от общего базового типа, тогда присвойте этому базовому типу виртуальный конструктор и вместо этого сохраните указатели этого типа.
Не нужно использовать умный указатель, он просто умный.
При этом, есть много других возможностей; единственное, что нужно сделать, это сохранить информацию о типе вдоль фактического объекта.
class Holder {
public:
template <typename T>
explicit Holder(T const volatile* t):
_data(static_cast<void const volatile*>(t)),
_delete(&Delete<T>)
{}
void apply() { _deleter(_data); }
private:
typedef void (*Deleter)(void const volatile*);
template <typename T>
static void Delete(void const volatile* p) {
delete static_cast<T const volatile*>(p);
}
void const volatile* _data;
Deleter _deleter;
};
И сейчас:
std::list<Holder> owningList;
owningList.push_back(Holder(somePointer));
for (Holder& h: owningList) { h.apply(); }
Правильный ответ на этот вопрос, конечно, «нет»
Изменить: попросили предоставить больше информации, хотя я уже сделал это в комментарии к вопросу, удаление пустоты * не определено, и этот вопрос является еще одним способом задать этот: Безопасно ли удалять пустой указатель? — см. ответы там для деталей.