Как избежать висящего указателя с shared_ptr? - PullRequest
1 голос
/ 09 июля 2019

У меня есть класс, указатели на объекты которого будут добавлены в качестве ключа / данных в несколько std :: map / std :: unordered_map / hash (реализовано внутри). Для автоматизации удаления объекта я использую shared_ptr.

Я разработал свой класс, используя shared_ptr только класс.

Теперь я хочу убедиться, что в будущем никто этого не сделает:

#include <memory>
#include <string>

class A {
 protected:
   struct this_is_private;

 public:
   explicit A(const this_is_private &) {}
   A(const this_is_private &, ::std::string, int) {}

   template <typename... T>
   static ::std::shared_ptr<A> create(T &&...args) {
      return ::std::make_shared<A>(this_is_private{0},
                                   ::std::forward<T>(args)...);
   }

 protected:
   struct this_is_private {
       explicit this_is_private(int) {}
   };

   A(const A &) = delete;
   const A &operator =(const A &) = delete;
};


::std::map<A*, int> m_error;
::std::map<::std::shared_ptr<A>, int> m_ok;

::std::shared_ptr<A> foo()
{
   ::std::shared_ptr<A> temp = A::create();

   A * obj_ptr = temp.get(); 
   m_error.insert(pair<A*, int>(obj_ptr, 10)); //How to make sure no one do this in future
   m_ok.insert(pair<::std::shared_ptr<A>, int>(temp,10)); //ok
}

Ответы [ 2 ]

4 голосов
/ 09 июля 2019

Как избежать висящего указателя с shared_ptr?

Не храня голые указатели (ни ссылки, ни итераторы) на объекте вообще, ни гарантируя, что время жизни такого указателя короче, чем время жизни общего указателя. Правильность последнего не так легко доказать, как правильность первого.

Как быть уверенным, что никто [не хранит голые указатели] в будущем

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

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

1 голос
/ 09 июля 2019

Скройте ваш shared_ptr в классе-оболочке "HiddenSharedPointer", чтобы пользователь вашего класса не получил прямого доступа к объекту.

#include <memory>
#include <list>
#include <utility>

class A 
{
public:
    class HiddenSharedPointer : private std::shared_ptr<A>
    {
    public:
        template<typename... Args>
        explicit HiddenSharedPointer(Args&&... args);
        HiddenSharedPointer(HiddenSharedPointer&) = default;
        HiddenSharedPointer(const HiddenSharedPointer&) = default;
        HiddenSharedPointer(HiddenSharedPointer&&) = default;
        HiddenSharedPointer& operator=(const HiddenSharedPointer&) = default;
        HiddenSharedPointer& operator=(HiddenSharedPointer&&) = default;

        // methods directly called on the underlying shared_ptr
        using std::shared_ptr<A>::reset;

        // methods called on the object referenced by the underlying shared_ptr
        void foo();
    };
private:
    explicit A()
    {}
    A(::std::string, int)
    {}
    A(const A &) = delete;
    const A &operator =(const A &) = delete;
public:
    template <typename... T>
    static HiddenSharedPointer create(T &&...args)
    {
        return HiddenSharedPointer(::std::forward<T>(args)...);
    }

    void foo()
    {
    }
};

void A::HiddenSharedPointer::foo()
{
    std::shared_ptr<A>(*this)->foo();
}

template<typename... Args>
A::HiddenSharedPointer::HiddenSharedPointer(Args&&... args)
    : std::shared_ptr<A>(new A(std::forward<Args>(args)...))
{}

std::list<std::pair<A::HiddenSharedPointer, int>> m_ok;

int main()
{
    A::HiddenSharedPointer temp = A::create();
    temp.foo();
    //auto plain_pointer_to_object = temp.get(); // does not compile
    m_ok.push_back(std::pair<A::HiddenSharedPointer, int>(temp, 10));

    temp.reset();

    return 0;
}

Обратите внимание, что я изменил карту на список пар, потому что вам нужно будет предоставить оператор <для класса HiddenSharedPointer, если вы используете карту. В любом случае, не рекомендуется использовать (совместно используемый) указатель в качестве ключа карты, поскольку это приводит к недетерминированному поведению (т. Е. При каждом запуске ваша карта имеет другой порядок). </p>

...