Shared_ptr странность с нулевыми значениями и пользовательским удалением
Недавно мы столкнулись с сбоем при переходе от unique_ptr к shared_ptr с использованием пользовательского делетера. Авария произошла, когда указатель, используемый для создания умного указателя, был нулевым. Ниже приведен код, который воспроизводит проблему, и показывает два случая, которые работают.
В приведенном ниже источнике One и Two работают счастливо, а три аварии в "ReleaseDestroy". Сбой, похоже, происходит, когда класс, используемый в интеллектуальном указателе, имеет виртуальную "Release", поэтому программа пытается найти V-таблицу. unique_ptr выглядит так, как будто он проверяет нулевые указатели и не запускает деструктор. Похоже, что общий указатель пренебрегает этим.
Кто-нибудь знает, если это по дизайну, или это ошибка в реализации stl? Мы используем Visual Studio 2015.
#include <iostream>
#include <memory>
template<class R>
void ReleaseDestroy(R* r)
{
r->Release();
};
class FlatDestroy
{
public :
void Release()
{
delete this;
}
};
class VirtualDestroy
{
public:
virtual void Release()
{
delete this;
}
};
class SimpleOne
{
public :
};
void main()
{
std::shared_ptr<SimpleOne> One(nullptr);
std::shared_ptr<FlatDestroy> Two(nullptr, ReleaseDestroy<FlatDestroy>);
std::shared_ptr<VirtualDestroy> Three(nullptr, ReleaseDestroy<VirtualDestroy>);
One.reset();
Two.reset();
Three.reset();
}
Ответы
Ответ 1
Поведение разрушения unique_ptr
и shared_ptr
отличается:
-
unique_ptr
только вызывает делеттер, если его удерживаемый указатель не равен нулю.
-
shared_ptr
всегда вызывает дебетер.
Таким образом, ваш общий деинтер указателя должен иметь возможность иметь дело с значениями нулевого указателя! Например, std::free
в порядке, но std::fclose
нет, и ни один из них не является вашим делетером (так как это разыменовывает r
безоговорочно).
Кстати, это появляется в LWG 2415, в котором рассматривается построение общего указателя с уникального указателя, который был разбит до этого разрешения дефекта именно по этой причине. (Я сам нахожу эту проблему , обратите внимание, как этот код тщательно различает нулевой регистр для shared_ptr
.)