简体   繁体   中英

How to avoid dangling pointer with shared_ptr?

I have a class whose object pointers will be added as a key/data in multiple std::map/std::unordered_map/hash(internally implemented). In order to automate deletion of the object I am using shared_ptr.

I have designed my class using shared_ptr only class.

Now I want to make sure that in future no one does this:

#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
}

How to avoid dangling pointer with shared_ptr?

By not storing bare pointers (nor references nor iterators) to the object at all, or ensuring that the lifetime of such pointer is shorter than the shared pointer's lifetime. Correctness of latter is not as easy to prove as the correctness of the former.

How to make sure no one [store bare pointers] in future

There is no feature in C++ language that would prevent taking the address of an object and storing it other than encapsulating access to the object completely. It is always the responsibility of the programmer who takes the address to make sure that the lifetime is - and will be in future - what they expect. And it is the responsibility of the programmer who changes the lifetime of an object to ensure that nothing depends on the changed lifetime.

There are programming language s, that have been designed so as to not let the programmer access the address of an object directly, thereby making this class of bug impossible. C++ is not one of those languages.

Hide your shared_ptr in a wrapper class "HiddenSharedPointer", so the user of your class gets no direct access to the object.

#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;
}

Note that I changed the map to a list of pairs, because you would have to provide the operator< for the HiddenSharedPointer class if using a map. Anyway, it's not a good idea to use a (shared) pointer as the key of a map as this imposes undeterministic behavior (ie in each run, your map has a different order).

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM