Можно ли реализовать не владеющий слегка умным указателем поверх стандартных слабых указателей?

Я думал, что, хотя я понимаю цели std::observer_ptr, я думаю, что это было бы хорошо, если бы была хотя бы опция для аналогичного типа указателя, который знает, было ли удалено то, на что он указывает. Например, у нас может быть что-то вроде следующего

slightly_smart_ptr<Foo> p1(new Foo());
auto p2 = p1;
p1.erase(); // This deletes the foo referred to by p1.
if (p2.expired())
   std::cout << "p2 is expired\n"; // this will fire

Один из способов добиться этого с помощью текущей стандартной библиотеки — создать shared_ptr для A в некоторой области, которая будет существовать в течение всего времени существования A, всегда ссылайтесь на A, передавая weak_ptr, и удаляйте A, когда он больше не нужен, путем сброса shared_ptr. Слабые_ptrs здесь будут иметь базовую семантику наблюдателя_ptrs, которые знают, был ли A удален. Но есть проблемы с этим подходом: weak_ptr должны быть заблокированы, превращая их в shared_ptrs для использования, что кажется неаккуратным, но, что более серьезно, shared_ptr для A должен где-то существовать, когда все, что пользователь хочет, это слегка умный указатель, который не владеет любой контент. Пользователь соглашается вручную уничтожить контент, когда придет время: никакие права собственности не являются общими, поэтому для пользователя является запахом кода, чтобы создать shared_ptr в такой ситуации.

Однако я не могу придумать, как можно было бы эффективно скрыть детали этой реализации.

Также существует ли такой указатель как предложение или в библиотеке повышения или где-либо еще?


person jwezorek    schedule 16.08.2019    source источник
comment
Я не уверен, что понимаю вопрос. Вы хотите, чтобы ваш пользовательский указатель получал уведомление, когда объект, на который он указывает, уничтожается, независимо от того, как он был размещен?   -  person HolyBlackCat    schedule 16.08.2019
comment
мне нужен пользовательский тип указателя, который, если p1 и p2 являются экземплярами типа, оба указывают на A, если я p1.reset(nullptr), это удаляет A - эту операцию не нужно называть сбросом, но вы поняли идею - тогда p2.expired() будет истинным.   -  person jwezorek    schedule 16.08.2019
comment
Звучит очень похоже на QPointer в Qt.   -  person Jesper Juhl    schedule 16.08.2019
comment
Сложный. Немалая часть того, почему слабый указатель требует блокировки, заключается в том, что выделение не освобождается частично при использовании выделения. Если вы только хотите знать, действителен ли указатель в этот точный момент времени? тогда запирание излишне, но я не вижу особого смысла в альтернативе. Распределение может исчезнуть сразу после теста. Вы после уведомления об освобождении?   -  person user4581301    schedule 16.08.2019
comment
Я действительно не вижу проблемы с использованием shared_ptr и weak_ptr. Независимо от того, как вы это спроектируете, вам нужно как-то заблокировать объект, чтобы один указатель не удалял его, пока другой использует его.   -  person super    schedule 16.08.2019
comment
если вы только хотите знать, действителен ли указатель в этот конкретный момент времени? тогда запирание излишне, но я не вижу особого смысла в альтернативе. --- Я имею в виду, что без блокировки это просто означало бы, что слегка_smart_ptr‹T› не будет потокобезопасным, верно? Но одним из серьезных критических замечаний в отношении shared_ptr является то, что вы платите за безопасность потоков, даже если она вам не требуется.   -  person jwezorek    schedule 16.08.2019
comment
@jwezorek не только это, но if(ptr.valid()) { stuff(); ptr->a; } Функция stuff() может сделать указатель недействительным, поэтому вам придется проверять перед каждым утверждением   -  person Guillaume Racicot    schedule 16.08.2019
comment
То, о чем вы просите, в общем случае трудно-невозможно, но без потоков (и при условии, что вы мало играете с прерываниями) вы просто ограничили варианты использования, чтобы устранить большинство действительно сложных проблем. Я рекомендую добавить это к вопросу.   -  person user4581301    schedule 16.08.2019
comment
Немного переосмыслив это. На этот вопрос есть отличные ответы, основанные на вопросе, как написано. Вам лучше написать новый вопрос, чем аннулировать их.   -  person user4581301    schedule 16.08.2019


Ответы (2)


Проблема такого умного указателя в том, что он более подвержен ошибкам, чем std::unique_ptr, T* или std::weak_ptr.

Если вы хотите знать, был ли указатель удален откуда-то еще его уникальным владельцем, на самом деле вам нужно совместное владение и std::weak_ptr.

Видите ли, есть причина, по которой вам нужно заблокировать слабый указатель перед его использованием. Это потому, что когда вы начинаете его использовать, вы получаете право собственности на указатель. Если вы не можете заблокировать свой указатель наблюдателя, который знает, удален он или нет, вы не можете безопасно его использовать, так как в любой момент после проверки его правильности он может быть удален.


Кроме того, у вас есть более глубокое противоречие.

Когда у вас есть уникальный указатель, вы знаете, кто его удалит, и вы знаете, кто его владелец.

Если у вас есть программа, которая проверяет действительность указателя во время выполнения, то это потому, что ваша программа не знает состояния владения ресурсом.

Если ваша программа или части вашей программы не могут знать состояние владения ресурсом и должны проверить, был ли он удален или нет, вам необходимо убедиться, что он не будет удален следующей строкой при его использовании, поскольку его можно удалить в любой момент, так как вы не можете знать о его статусе владения. Поэтому вам необходимо временно владеть ресурсом во время его использования. Поэтому вам нужно совместное владение, чтобы отложить решение о владении при выполнении кода.

Если у вас совместное владение, вам не нужен указатель наблюдателя, который знает, удален он или нет.

Тогда ваш указатель не должен существовать.


Итак... вы подумали, что вам нужен этот указатель, он может пригодиться... что вы можете сделать?

Вам нужно пересмотреть свой код. Если есть одно владение, зачем вам знать действительность указателя. Почему нельзя просто спросить владельца?

Если владелец не существует, возможно, ваш код, который хочет выполнить проверку, не должен быть действительным, когда владелец удален. Возможно, ваша структура, которая хочет сделать проверку, должна умереть одновременно с владельцем.

Если ваш уникальный владелец умирает в непредсказуемый момент (например, ваш уникальный владелец принадлежит общему владельцу), то, возможно, ваша структура должна вместо этого проверять действительность общего владельца.

Возможно, ваш код, вызывающий функцию, которая хочет проверить, действителен ли ее указатель, должен просто не вызывать ее, когда ее владелец мертв.

...

И так далее.

Есть так много способов решить эту проблему, но потребность в слабом указателе на уникального владельца обычно указывает на недостаток в программе или проблему в рассуждениях о времени жизни объектов в вашей программе.

person Guillaume Racicot    schedule 16.08.2019

Не осуществимо в общем.

Вся цель существующих интеллектуальных указателей состоит в том, чтобы отслеживать время жизни объекта и владение им таким образом, который просто невозможен в общем случае с необработанными указателями, если только вы не подключаетесь к распределителю и не имеете какой-то запутанной связи между этим распределителем и любыми дескрипторами, относящимися к нему. к выделенному объекту.

Преимущества, которые вы описываете, - это преимущества, которые возникают благодаря использованию существующих интеллектуальных указателей. shared_ptr и weak_ptr здесь идеальны.

Нет проблем с блокировкой (вы этого хотите) и нет проблем с тем, что где-то должен быть shared_ptr, потому что наверняка кто-то где-то владеет этими данными. Если они этого не делают, у вашего проекта есть гораздо более серьезные проблемы, и вы пытаетесь обойти эти проблемы с помощью аналогичной сломанной концепции интеллектуального указателя, которой никогда не будет в стандарте.

person Lightness Races in Orbit    schedule 16.08.2019