Why shared_ptr can access using a "secure access right",

I did some testing using shared_ptr and I can not come up with this question below. I just started learning boost library. Can anyone tell me the reason?

#include <boost\shared_ptr.hpp> #include <iostream> class A { public: virtual void sing() { std::cout<<"A"; } protected: virtual ~A() {}; }; class B : public A { public: virtual void sing() { std::cout << "B"; } virtual ~B() {}; }; int foo() { boost::shared_ptr<A> pa(new B()); pa->sing(); delete static_cast<B*>(pa.get()); delete pa.get(); //this line has a problem error C2248: "A::~A": can't access protected memmber(declared in class"A") return 0; } int main() { foo(); return 0; } 

but it can be compiled when this line is commented out. Of course, this does not mean that shared_ptr will remove the pointer internally supported from the main function, like what I did. Is there any difference between the pointer returned by pa.get() and the internal storage of the pointer?

+6
source share
4 answers

I believe that delete is called during the destruction of shared_ptr by the type of the pointer passed to the constructor. Take a look at the constructor here:

http://www.boost.org/doc/libs/1_49_0/libs/smart_ptr/shared_ptr.htm#constructors

So, when your pa goes out of scope, B::~B( ) is called, and not the destructor of the type it contains - A::~A (which would be impossible, since it declared protected ).

+3
source

This is actually a bit more complicated: the mechanism behind shared_ptr is pretty complicated.

First, let's prove that there are no special permissions granted to shared_ptr :

 int main() { A* a = new B(); std::shared_ptr<A> p(a); // expected-error } 

This will result in an error because destructor A not available. Interestingly, an error occurs at the point of construction, which is the key ...

So what is the magic behind shared_ptr ?

Inside, a shared_ptr stores much more than a simple pointer and reference count. A shared_ptr built with a deleter responsible for destroying an instance of an object. Where the design really shines, it is that this deleter is created in the constructor and therefore can know more information about the type than the bare type shared_ptr allows.

Simplified demo:

 template <typename T> struct shared_holder { typedef void (*Disposer)(T*); explicit shared_holder_base(T* t, Disposer d): _ptr(t), _disposer(d) {} void dispose() { _disposer(_ptr); _ptr = 0; } T* _ptr; Disposer _disposer; }; template <typename U, typename T> void dispose(T* t) { delete static_cast<U*>(t); } template <typename T> class shared_ptr { typedef shared_holder<T> holder; public: shared_ptr(): _holder(0), _ptr(0) {} template <typename U> explicit shared_ptr(U* u): _holder(new holder(u, dispose<U, T>)), _ptr(_holder->_ptr) {} private: holder* _holder; T* _ptr; }; 

The key understanding is that the removal tool is created from a static type known to the constructor; that's why:

  • shared_ptr<A>(new B) works
  • A* a = new B; shared_ptr<A>(a) A* a = new B; shared_ptr<A>(a) not

You can read the Boost headers, the hardware behind shared_ptr pretty interesting.

As an exercise for the reader, why does shared_ptr<T> have the _ptr member?

+2
source

If you have:

 boost::shared_ptr<A> pa(new B()); 

... you call the constructor boost::shared_ptr and deal with two template parameters:

  • shared_ptr T template ( A in your case);

    get() returns T* , so when you tried:

     delete pa.get(); 

    ... you tried to access ~A() , which is only accessible to children of A and therefore received an error.

    In the next line:

      delete static_cast<B*>(pa.get()); 

    ... you dumped A* to B* and called delete on B* , while calling ~B() , to which you had access. ( ~B() always calls ~A() for ~A() declared as virtual )

  • template for the shared_ptr constructor template of the Y constructor ( B in your case) with the help that Y* must be converted to T* (you can raise B* to A* as B inherits A in your case).

    ~shared_ptr() calls delete on Y* ( B* in your case; ~B() can access ~A() and calls it), and this is what happens when pa goes outside the scope (and that’s how shared_ptr turned to a protected destructor protected by the base class, which was your original question).

boost::shared_ptr stores one pointer internally. When creating boost::shared_ptr you pass a SINGLE pointer to its constructor, which can be considered as / convert to a pointer to any of these types of TWO strong> arguments .

+1
source

I believe this is because the definition of the boost template declares the sharepointer to be its friend of the <Class T> class from your template.

This is a snipet that I copied from the header file of the downloaded generic pointer.

 // Tasteless as this may seem, making all members public allows member templates // to work in the absence of member template friends. (Matthew Langston) #ifndef BOOST_NO_MEMBER_TEMPLATE_FRIENDS private: template<class Y> friend class shared_ptr; template<class Y> friend class weak_ptr; #endif 
0
source

Source: https://habr.com/ru/post/914622/


All Articles