Почему нет «слабого указателя» для необработанного указателя?Или есть? - PullRequest
0 голосов
/ 03 апреля 2019

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

Итак, мой вопрос: не было ли попытки добавить в современный c ++ «слабый указатель», который не зависит от использования общих указателей? Я имею в виду просто указатель, который становится NULL при удалении в любой части программы. Есть ли причина не использовать такую ​​самодельную обертку?

Чтобы лучше объяснить, что я имею в виду, ниже приведен такой "слабый указатель", который я сделал. Я назвал его WatchedPtr.

#include <memory>
#include <iostream>

template <typename T>
class WatchedPtr {
public:
    // the only way to allocate new pointer
    template <typename... ARGS>
    WatchedPtr(ARGS... args) : _ptr (new T(args...)), _allocated (std::make_shared<bool>(true)) {}

    WatchedPtr(const WatchedPtr<T>& other) : _ptr (other._ptr), _allocated (other._allocated) {}

    // delete the pointer
    void del () {delete _ptr; *_allocated = false;}

    auto& operator=(const WatchedPtr<T> &other) { return *this = other; }

    bool isNull() const { return *_allocated; }

    T* operator->() const { return _ptr; }

    T& operator*() const { return *_ptr; }

private:
    T* _ptr;
    std::shared_ptr <bool> _allocated;
};

struct S {
    int a = 1;
};

int main () {
    WatchedPtr<S> p1;
    WatchedPtr<S> p2(p1);
    p1->a = 8;
    std::cout << p1.isNull () << std::endl;
    std::cout << p2.isNull () << std::endl;
    p2.del ();
    std::cout << p1.isNull () << std::endl;
    std::cout << p1.isNull () << std::endl;
    return 0;
}

Результат:

1
1
0
0

-Edited-

Спасибо всем. Некоторые пояснения после комментариев и ответов на данный момент:

  • Реализация, которую я представил для WatchedPtr, просто демонстрирует, что я имею в виду: указатель, который не получает копию из внешнего размещения, не может быть удален извне и становится нулевым, если он удаляется. Реализация заведомо далека от совершенства и не должна была быть идеальной.
  • Проблема со смесью shared_ptr и необработанных указателей очень распространена: A * хранится как необработанный указатель, поэтому создается в некоторой точке программы и явно удаляется в некоторой точке программы. B содержит A *, а B * - shared_ptr, поэтому B * имеет неопределенную продолжительность жизни. Таким образом, B может жить долго после удаления A *, которое имеет место.
  • Основное использование «WatchedPtr» в моем сознании - это защитное программирование. то есть проверить на нулевое значение и сделать все возможное для обеспечения непрерывности (и ошибки отладки). shared_ptr может сделать это, но очень опасно - это скроет и задержит проблему.
  • Также может быть использование дизайна для «WatchedPtr» (очень мало и явных «владельцев»), но это не главная идея. Для этого действительно общие указатели делают работу.
  • Целью «WatchedPtr» не является замена всех существующих необработанных указателей в программе сразу. Это не то же самое, что замена на shared_ptr, что IMHO было сделано для всей программы сразу. Что нереально для крупномасштабных программ.

Ответы [ 3 ]

5 голосов
/ 03 апреля 2019

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

Можно предположить расширение, скажем, unique_ptr, которое, конечно, поддерживает слабые указатели. Предположительно, главная причина, по которой никто не торопился реализовать такую ​​функцию, заключается в том, что слабые указатели уже находятся в конце шкалы «Используйте пересчет, и все должно работать», в то время как unique_ptr - в «Управляйте своими жизнями через RAII или вас». Вы не настоящий C ++ программист "конец шкалы. Слабые указатели также требуют наличия отдельного блока управления для каждого выделения, а это означает, что преимущество в производительности такого WatchedPtr будет минимальным по сравнению с shared_ptr.

2 голосов
/ 03 апреля 2019

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

Тогда вы говорите

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

Разве вы не видите противоречие?

Вы не хотите использовать разделяемый указатель, поскольку время жизни объектов определяется во время выполнения. Пока все хорошо.

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

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

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

std::shared_ptr + std::weak_ptr предназначен для решения проблемы «части вашей программы не знают, когда владелец освободит ресурс». Что вам нужно, это один std::shared_ptr и несколько std::weak_ptr, чтобы они знали, когда ресурс освобожден. Эти классы имеют информацию, необходимую для проверки времени жизни переменной во время выполнения.

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

0 голосов
/ 11 мая 2019

Читая ответы и комментарии, а также Основные руководящие принципы C ++ Бьярна Страуструпа и Херба Саттера , я пришел к следующему ответу:
При соблюдении руководящих указаний нет необходимости в "WatchedPtr ", который включает" новый "и" удалить ".Тем не менее, способ отследить достоверность необработанного указателя, взятого из умного указателя, все еще находится под вопросом для целей отладки / контроля качества.

Подробнее:

Необработанные указатели должны продолжатьсяиспользоваться.По разным причинам.Однако явных «new» и «delete» не следуетСлучаи вызова «new» и «delete» должны быть заменены на shared_ptr / unique_ptr.
В месте, где в настоящее время размещен необработанный указатель, нет смысла заменять его на «WatchedPtr».
Еслизаменяя необработанный указатель на что-то еще, где он размещен, в большинстве случаев это будет уникальным_птр, а в других случаях - общим_птр.«WatchedPtr», если это вообще так, будет продолжаться с этой точки, построенный из общего / уникального указателя.

Для этого я разместил несколько другой вопрос .

...