Проблема: Unique_ptrs express владеет хорошо, но не может отслеживать время жизни своих объектов слабых_птров. Shared_ptrs может отслеживаться слабым_птр, но не является express владельцем явно.
Предлагаемое решение: Получить новый тип указателя (я буду называть его strong_ptr), который является просто shared_ptr, но с конструктором копирования и Оператор присваивания удален, так что его сложно клонировать. Затем мы создаем еще один новый тип loaned_ptr (который не может быть легко сохранен) для обработки временного продления времени жизни, необходимого при доступе к объекту weak_ptr, и тем самым можем избежать явного использования shared_ptr в любом месте.
Этот вопрос Не Копии владельца std :: unique_ptr и этот Лучше shared_ptr по разным типам для "владения" и "ссылки"? оба похожи, но в обоих случаях выбор оформлен как просто unique_ptr против shared_ptr и ответ не предлагает удовлетворительное решение для моего ума. (Возможно, мне следует отвечать на эти вопросы вместо того, чтобы задавать новый? Не уверен, каков правильный этикет в этом случае.)
Вот базовый c удар. Обратите внимание, что для того, чтобы пользователю слабого указателя не пришлось преобразовываться в shared_ptr для его использования, я создаю тип loaned_ptr (спасибо ржавчине за имя), который оборачивает shared_ptr, но затрудняет случайное его сохранение пользователем. Таким образом, используя по-разному производные hamstrung shared_ptr, мы можем express предполагаемое владение и направлять клиентский код в правильное использование.
#include <memory>
template <typename T>
// This owns the memory
class strong_ptr : public std::shared_ptr<T> {
public:
strong_ptr() = default;
strong_ptr(T* t) : std::shared_ptr<T>(t) {}
strong_ptr(const strong_ptr&) = delete;
strong_ptr& operator=(const strong_ptr&) = delete;
};
template <typename T>
// This can temporarily extend the lifetime but is intentionally hard to store
class borrowed_ptr : public std::shared_ptr<T> {
public:
borrowed_ptr() = delete;
borrowed_ptr(const borrowed_ptr&) = delete;
borrowed_ptr& operator=(const borrowed_ptr&) = delete;
template <typename T>
static borrowed_ptr borrow(const std::weak_ptr<T>& wp)
{
return wp.lock();
}
private:
borrowed_ptr(std::shared_ptr<T> &sp) : std::shared_ptr<T>(sp) {}
};
Это кажется довольно простым и улучшением по сравнению с shared_ptr, но я не могу найти никакого обсуждения такая техника, поэтому я могу только представить, что я упустил очевидный недостаток.
Может кто-нибудь дать мне конкретную причину, почему это плохая идея? (И да, я знаю, что это менее эффективно, чем unique_ptr - для PIMPL и т. Д. Я бы по-прежнему использовал unique_ptr.)
Предостережение: я еще не использовал это больше, чем базовый c пример, но это компилируется и работает нормально:
struct xxx
{
int yyy;
double zzz;
};
struct aaa
{
borrowed_ptr<xxx> naughty;
};
void testfun()
{
strong_ptr<xxx> stp = new xxx;
stp->yyy = 123;
stp->zzz = 0.456;
std::weak_ptr<xxx> wkp = stp;
// borrowed_ptr<xxx> shp = wkp.lock(); <-- Fails to compile as planned
// aaa badStruct { borrowed_ptr<xxx>::borrow(wkp) }; <-- Fails to compile as planned
// aaa anotherBadStruct; <-- Fails to compile as planned
borrowed_ptr<xxx> brp = borrowed_ptr<xxx>::borrow(wkp); // Only way to create the borrowed pointer
// std::cout << "wkp: " << wkp->yyy << std::endl; <-- Fails to compile as planned
std::cout << "stp: " << stp->yyy << std::endl; // ok
std::cout << "bp: " << brp->yyy << std::endl; // ok
}