Как я могу вызвать частный деструктор из shared_ptr?

У меня есть класс resource_manager, который поддерживает std::vector<boost::shared_ptr<resource> > внутри. resource_manager является другом класса resource. Я хочу, чтобы resource создавался/удалялся только resource_manager, поэтому я сделал его конструкторы закрытыми (что работает нормально).

Однако, если я сделаю деструктор приватным, код не скомпилируется, потому что деструктор вызывается boost::shared_ptr, который не является другом resource. Я думаю о применении правила «не удалять клиентами», возвращая только const resource* из resource_manager, но почему-то я не удовлетворен безопасностью, которую обеспечивает этот метод (что, если клиент каким-то образом произойдет через указатель на неконстантный? )

Помимо очевидного решения не использовать shared_ptr, есть ли у вас какое-либо обходное/лучшее решение моей проблемы?


person Dan Nestor    schedule 20.11.2011    source источник
comment
client каким-то образом происходит через указатель на неконстантный, единственный способ, которым они могут это сделать, - это если они используют const_cast. С точки зрения безопасности нет никакой разницы между необработанными указателями и shared_ptr, разница заключается в управлении временем жизни, с которым я не уверен, как бы вы справились без shared_ptr.   -  person ronag    schedule 20.11.2011
comment
const resource* не означает, что вы не можете его удалить.   -  person UncleBens    schedule 20.11.2011
comment
@UncleBens: Это не так? Я думал, что компилятор не должен позволять вам вызывать неконстантную функцию для константы! Я ошибаюсь? Или это правило не распространяется на деструктор?   -  person Dan Nestor    schedule 20.11.2011
comment
Это правило не применяется к деструкторам, потому что иначе вы не сможете уничтожить объекты, которые являются const.   -  person MSalters    schedule 21.11.2011


Ответы (2)


Вы можете передать пользовательское средство удаления в общий указатель. Поэтому просто создайте функтор или функцию удаления (на ваше усмотрение), которая, в свою очередь, является friend вашего класса:

class Secret
{
  ~Secret() { }
  friend class SecretDeleter;
  friend void SecretDelFunc(Secret *);
};

class SecretDeleter
{
public:
  void operator()(Secret * p) { delete p; }
};

void SecretDelFunc(Secret * p) { delete p; }

std::shared_ptr<Secret> sp1(new Secret, SecretDeleter());
std::shared_ptr<Secret> sp2(new Secret, SecretDelFunc);
person Kerrek SB    schedule 20.11.2011
comment
Почему ты всегда быстрее меня с одной и той же идеей? Прекрати это! :( - person Xeo; 20.11.2011
comment
@Xeo: Лучше возьми другой кусок пирога - я пристально смотрю на shared_ptr! ;-) - person Kerrek SB; 20.11.2011
comment
Кстати, вы должны создать экземпляр SecretDeleter. - person Xeo; 20.11.2011
comment
@Xeo: Готово. Я предполагаю, что свободная функция была бы даже проще, чем класс функтора. Я тоже добавлю. (На самом деле, в С++ 11 есть загадочный третий вариант создания специального распределителя памяти, который является другом, и использования std::allocate_shared.) - person Kerrek SB; 20.11.2011
comment
Большое спасибо! Я понятия не имел, что ты можешь это сделать. - person Dan Nestor; 20.11.2011
comment
Есть ли менее болезненный способ, чем создание автономных объектов и/или функций удаления. Я думаю, это довольно распространенная идиома - person zzz777; 17.01.2015
comment
@Керрек СБ. Я не знаю о вашем опыте. Помимо меня, у нас есть еще более 2000 инженеров-программистов, и я хотел бы (а) быть уверенным, что никто не может вызвать удаление для объекта, управляемого с помощью shared_ptr, и (б) не вводить пользовательское средство удаления для каждого объекта, который я пишу, и (c ) использовать преимущества производительности и безопасности make_shared. Возможно, это характерно для крупных магазинов. - person zzz777; 19.01.2015
comment
@ zzz777: Почему для таких вещей недостаточно простого частного конструктора? Ты защищаешься от Мерфи, а не от Макиавелли. - person Kerrek SB; 20.01.2015
comment
@Керрек СБ. Мне не хватает чего-то большого. Естественно, в такой среде все конструкторы являются приватными, но как приватный конструктор может помочь предотвратить непреднамеренное удаление, я совершенно не понимаю, извините. - person zzz777; 20.01.2015
comment
@zzz777: я просто думаю, что если вы используете современные идиомы C++ в своей кодовой базе, то необработанные delete встречаются достаточно редко, чтобы такие ошибочные удаления не были непреднамеренными: и shared_ptr, и delete являются инструментами специального назначения в C++11, которые следует использовать с осторожностью, а пересечение еще меньше (например, наличие долгоживущих необработанных указателей на общие указатели). Поэтому я не уверен, есть ли здесь реальная проблема, которую стоит решить, и есть ли много реальных ошибок, вызванных этим (которые не вызваны в целом неясной семантикой времени жизни в стиле кода). - person Kerrek SB; 20.01.2015
comment
@Керрек СБ. Итак, вы согласны со мной по существу. Теперь о практических последствиях использования современного C++. Никто не может знать о 99,9999% реальных ошибок в любой реальной компании-разработчике программного обеспечения. И вдобавок ко всему у вас нет возможности узнать о какой-либо реальной ошибке в какой-либо функции, которая НЕ используется, потому что люди, принимающие решения, считают это ненужным громоздким. Здесь нет ничего нового - люди, разрабатывающие C++, любят, чтобы их вещи были очень громоздкими. - person zzz777; 21.01.2015

Возможно объявить shared_ptr<resource> другом? shared_ptr не вызывает конструктор и должен уничтожаться только в том случае, если ваш менеджер ресурсов освобождает указатель до того, как все клиенты уничтожили свои shared_ptr. Это не позволит клиентам нарушить защиту, но позволит клиентам поддерживать работоспособность ресурса против «воли» resource_manager.

person 01d55    schedule 20.11.2011