#c #pointers
#c #указатели
Вопрос:
shared_ptr<void>
особенность в том, что он, по определению, будет вызывать неопределенное поведение при вызове delete
a void*
.
Итак, почему нет shared_ptr<void>
специализации, которая выдает ошибку компиляции?
Комментарии:
1. Зачем нужна специализация? Разве это уже не выдает ошибку компилятора?
2. @MooingDuck Действительно невозможно объяснить, почему
delete (void*)0;
это было разрешено комитетом C . Или, может быть, это была шутка, которую разработчики восприняли всерьез.3. Как ты думаешь, почему это невозможно, @Curiousguy? Это не значит, что все члены комитета мертвы и не оставили заметок. Я уверен, что большинство из них все еще живы, и, вероятно, есть протоколы собраний и переписка. Это не древняя история. Кто-то, желающий приложить усилия для его исследования, вероятно, мог бы точно сказать, почему это так.
Ответ №1:
Использование shared_ptr для хранения произвольного объекта
shared_ptr может действовать как универсальный указатель на объект, аналогичный void* . Когда экземпляр shared_ptr создается как:
shared_ptr<void> pv(new X);
уничтожается, он правильно удалит объект X, выполнив ~X.
Это свойство можно использовать почти так же, как необработанный void* используется для временного удаления информации о типе из указателя объекта. shared_ptr позже может быть возвращен к правильному типу с помощью static_pointer_cast .
Но как?
Ответ №2:
shared_ptr<T>
особенность в том, что по дизайну разрешено хранить указатель на любой тип указателя, который преобразуется в T*
и будет использовать соответствующий параметр удаления без UB! Это связано со shared_ptr<Base> p(new Derived);
сценариями, но также включает shared_ptr<void>
.
Например:
#include <boost/shared_ptr.hpp>
struct T {
T() { std::cout << "T()n"; }
~T() { std::cout << "~T()n"; }
};
int main() {
boost::shared_ptr<void> sp(new T);
}
выдает вывод:
$ ./test
T()
~T()
Если вы посещаете http://www.boost.org/doc/libs/1_47_0/libs/smart_ptr/shared_ptr.htm , прокрутите вниз до раздела задания, чтобы увидеть, что именно демонстрируется. См. http://www.boost.org/doc/libs/1_47_0/libs/smart_ptr/sp_techniques.html#pvoid для получения более подробной информации.
РЕДАКТИРОВАТЬ как отметил trinithis, это UB, если тип указателя, переданный в конструктор, является void *
указателем. Спасибо, что указали на это!
Комментарии:
1. Пожалуйста, обратите внимание, что это UB, если тип указателя, переданный в конструктор, является
void*
указателем.2.Даже если тип переданного указателя
void*
равен, разве вы не могли бы избежать неопределенного поведения, используя конструктор с двумя аргументами для передачи пользовательского средства удаления, которое выполняет что-то другое, кроме вызоваdelete
?3. Применим ли этот ответ только к
boost::shared_ptr
orstd::shared_ptr
также? Я не могу найти правильную ссылку.4. @Mark Ransom: насколько я знаю
std::shared_ptr
, имеет ту же функцию.5. @Rob: Конечно, но это все равно будет аргументировать специализацию
std::shared_ptr<void>
, в которой устранен ctor с одним аргументом. Естьget_deleter
но нетset_deleter
, поэтому, если вы не передадите удаление во время построения, это приведет к неопределенному поведению позже.
Ответ №3:
Если ваш указатель был создан с помощью чего-то подобного malloc
, вы можете сделать shared_ptr<void, dtor>
, где dtor
вызовы free
. Это приведет к определенному поведению.
Но опять же, возможно, вам нужно неопределенное поведение: D
Ответ №4:
Но это действительно во многих случаях. Рассмотрим следующее:
class T
{
public:
~T() { std::cout << "~Tn"; }
};
int main()
{
boost::shared_ptr<void> sp(new T);
}
В обстоятельствах, когда вы пытаетесь передать подлинное void *
значение в shared_ptr
конструктор, вы должны получить ошибку компиляции.