I made some testing with shared_ptr,and i can't think out the matter below.I just started to learn the boost library. Is there anybody can 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 that line is commented out. Surely it doesn't mean that the shared_ptr will delete the pointer internally maintained out of the main function, just like what i did. Is there any difference between the pointer returned by pa.get() and the pointer internally maintained?
I believe that delete is called during destruction of the shared_ptr on the type of the pointer passed into the constructor. Have 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 rather than the destructor of the type contained - A::~A ( which would be impossible because it's declared protected).
Actually, it's a bit more complicated than that: the machinery behind shared_ptr is quite complicated.
First, let us prove there is no specific access rights 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 the destructor of A is not accessible. Interestingly, the error occurs at the point of construction, which is a clue...
So, what is the magic behind shared_ptr ?
Internally, a shared_ptr keeps much more than a simple pointer and reference counts. A shared_ptr is built with a deleter, in charge of destructing the instance of the object. Where the design really shines is that this deleter is instantiated in the constructor and thus may know more type information than the bare shared_ptr type lets on.
A 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 insight is that the disposer is instantiated from the static type known by the constructor; this is why:
shared_ptr<A>(new B) works
A* a = new B; shared_ptr<A>(a) does not
You can read the Boost headers, the machinery behind the shared_ptr is quite interesting.
As an exercise for the reader, why does shared_ptr<T> has a _ptr member ?
When you have:
boost::shared_ptr<A> pa(new B());
...you are calling boost::shared_ptr constructor and are dealing with TWO template parameters:
shared_ptr template type T (A in your case);
get() returns T* so when you tried:
delete pa.get();
...you tried to access ~A() which is accessible only to As children and therefore got an error.
In the following line:
delete static_cast<B*>(pa.get());
...you were downcasting A* to B* and called delete on B* thus invoking ~B() to which you had access. (~B() is always calling ~A() for ~A() is declared as virtual)
shared_ptr constructor argument template type Y (B in your case) with the
requirement that Y* must be convertible to T* (you can upcast
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 out of scope (and this is how shared_ptr accessed base class protected destructor which was your original question).
boost::shared_ptr keeps internally a single pointer. When creating boost::shared_ptr you are passing to its constructor a SINGLE pointer which can be regarded as / converted to pointer to any of those TWO template argument types.
I believe it is because the definition of the boost template declares the sharedpointer a friend of the <Class T> class from your template instantiation.
This is a snipet I copied from the boot shared pointer header file.
// 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
Related
I was reading the Smart Pointer Programming Techniques provided in the boost documentation.
In the Section "using abstract classes for implementation hiding", they provide a nice idiom to fully hide an implementation behind pure virtual interface. For example:
// Foo.hpp
#include <memory>
class Foo {
public:
virtual void Execute() const = 0;
protected:
~Foo() = default;
};
std::shared_ptr<const Foo> MakeFoo();
and
// Foo.cpp
#include "Foo.hpp"
#include <iostream>
class FooImp final
: public Foo {
public:
FooImp() = default;
FooImp(const FooImp&) = delete;
FooImp& operator=(const FooImp&) = delete;
void Execute() const override {
std::cout << "Foo::Execute()" << std::endl;
}
};
std::shared_ptr<const Foo> MakeFoo() {
return std::make_shared<const FooImp>();
}
Regarding the protected, non-virtual destructor in class Foo, the document states:
Note the protected and nonvirtual destructor in the example above. The
client code cannot, and does not need to, delete a pointer to X; the
shared_ptr<X> instance returned from createX will correctly call
~X_impl.
which I believe I understand.
Now, it seems to me that this nice idiom could be used to produce a singleton-like entity if a factory function returned std::unique_ptr<Foo>; the user would be forced to move the pointer, with a compile-time guarantee that no copies exist.
But, alas, I cannot make the code work unless I change ~Foo() = default from protected to public, and I do not understand why.
In other words, this does not work:
std::unique_ptr<const Foo> MakeUniqueFoo() {
return std::make_unique<const FooImp>();
}
My questions:
Could you explain me why do I need to make public ~Foo() = default?
Would it be dangerous to just remove protected?
Is the singleton-like idea even worth it?
The issue has to do with how deleters work in smart pointers.
In shared_ptr, the deleter is dynamic. When you have std::make_shared<const FooImp>();, the deleter in that object will call ~FooImpl() directly:
The object is destroyed using delete-expression or a custom deleter that is supplied to shared_ptr during construction.
That deleter will be copied onto the shared_ptr<const Foo> when it's created.
In unique_ptr, the deleter is part of the type. It's:
template<
class T,
class Deleter = std::default_delete<T>
> class unique_ptr;
So when you have unique_ptr<const Foo>, that will call ~Foo() directly - which is impossible since ~Foo() is protected. That's why when you make Foo() public, it works. Works, as in, compiles. You would have to make it virtual too - otherwise you'd have undefined behavior by only destructing the Foo part of FooImpl.
It's not dangerous. Unless you forget to make the destructor virtual, which, it bears repeating, will cause undefined behavior.
This isn't really singleton-like. As to whether or not it's worth it? Primarily opinion based.
Every shared_ptr stores 4 things: a pointer, a strong reference count, a weak reference count, and a deleter.
The deleter gets the type from which the shared_ptr was constructed from, and deletes that type, not the exposed type. If you cast it to a base shared_ptr, the derived deleter is still stored.
unique_ptr does not, by default, store such a stateful deleter.
The design reason behind this is that a shared_ptr is already managing extra resources: adding that deleter is cheap, given that you are already managing the reference counts.
For unique_ptr, without a stateful deleter, the overhead for it is basically identical to a raw pointer. Adding a stateful deleter by default would make unique_ptrs significantly more expensive.
While they are both smart pointers, unique_ptr is really minimal, while shared_ptr is far more complex.
You can get around this by appending a stateful deleter to unique_ptr.
struct stateful_delete {
void const* ptr = nullptr;
void(*f)(void const*) = nullptr;
template<class T>
stateful_delete(T const* t):
ptr(t),
f([](void const* ptr){
delete static_cast<T const*>(ptr);
})
{}
template<class T>
void operator()(T*)const{
if (f) f(ptr);
}
};
template<class T>
using unique_ptr_2 = std::unique_ptr<T, stateful_delete>;
template<class T>
unique_ptr_2<T> unique_wrap_2(T* t) {
return {t, t};
}
template<class T, class...Args>
unique_ptr_2<T> make_unique_2(Args&&...args) {
return unique_wrap( new T(std::forward<Args>(args)...) );
}
such unique_ptr_2 are 3 times as large as a unique_ptr. They do not do an extra allocation (unlike shared_ptr). And they will work with your non-virtual protected ~Foo with a public ~FooImpl.
You could reduce the size of unique_ptr_2 down to 2 pointers if we use the make_shared technique of doing a unified allocation, and store the equivalent of the ptr and f on the heap. I'm uncertain if that complexity is worth the savings.
Based on Barry's answer an alternative to making it public is defining your own deleter which has access to your class' ~Foo() method.
Example (tried with VS2013):
template <typename T>
class deleter
{
public:
void operator()(T* a)
{
// Explicitly call the destructor on a.
a->~A();
}
};
class A {
friend class deleter<A>; // Grant access to the deleter.
protected:
~A() {
// Destructor.
}
};
std::unique_ptr<A, deleter<A>> MakeA()
{
return std::unique_ptr<A, deleter<A>>(new A());
}
Here's a toy example illustrating a problem I encounter. The application is fairly irrelevant (it's essentially a linked list of elements with a special behavior at the end). I'm unable to construct a base class shared_ptr with a derived pointer and it's for some reason linked to the fact that I'm using private inheritance.
#include <iostream>
#include <boost/shared_ptr.hpp>
#include <boost/make_shared.hpp>
using namespace std;
// An Item in a linked list
class A
{
public:
//friend class B;
typedef boost::shared_ptr<A> APtr;
A() : next_() {}
A(APtr n) : next_(n) {}
APtr next() { return next_; }
void setNext(APtr n) { next_ = n; }
virtual void doIt() { /* standard behavior */ }
private:
APtr next_;
};
class B : private A // B really is a special A
// that should have different behavior
// at the tail of the chain
// but I want to hide A's interface
// to external clients
{
public:
typedef boost::shared_ptr<B> BPtr;
B(A::APtr prev)
{ // Set this object as the tail
prev->setNext(APtr(this)); /* WHY CAN'T I CONSTRUCT APtr(this)
WITH PRIVATE INH. */
}
void doIt() {/*special behavior at end */}
};
int main()
{
A::APtr dummyPtr;
A::APtr head = boost::make_shared<A>(dummyPtr);
B::BPtr tail = boost::make_shared<B>(head);
for(A::APtr curr = head; curr; curr=curr->next()){
curr->doIt();
}
return 0;
}
and I get this
/usr/include/boost/smart_ptr/shared_ptr.hpp: In constructor ‘boost::shared_ptr<T>::shared_ptr(Y*) [with Y = B, T = A]’:
derived_shared.cpp:31: instantiated from here
/usr/include/boost/smart_ptr/shared_ptr.hpp:352: error: ‘A’ is an inaccessible base of ‘B’
I was under the impression that private inheritance allows the Derived class to still access the public interface of the base class but hides that interface to external clients. Why does private inheritance cause this error (it works if I inherit publicly)?
Change this single line:
prev->setNext(APtr(this));
to
prev->setNext(APtr(static_cast<A*>(this)));
And it compiles.
Or at least it does when using the std library. It is usually similar for boost.
There are other errors but that gets round casting B* to A*.
Why does that work? Because the template for the constructor std::shared_ptr<A> isn't what you think it is! It is more like template <class X> std::shared_ptr(X* v). So the actual B* to A* cast is postponed and failing in a non-friend member.
But if you cast the B* pointer (i.e. this) to A* inside a method of class B (the only place that is legal without a friend declaration) you're in.
NB: There is nothing in principle wrong with private inheritance. It isn't an anti-pattern and is provided for good reason. Do think about composition but objects that forbid some parts of the application from 'accessing' their 'real' type have lots of uses. For example pass out an object A that has some B bolt ons that only the object factory can access.
PS: The reason why the constructor is template<class T> shared_ptr<T* v> is so that shared_ptr uses the deleter of the type passed into it.
As you're not doubt aware share_ptr cleverly calls the 'right' destructor even if it isn't virtual.
My 'fix' actually subverts that cleverness so beware to pass in the right deleter or (recommended) make the destructor of A virtual.
PPS:
And finally a fully working program (using STL. Sorry I don't have Boost):
#include <iostream>
#include <memory>
// An Item in a linked list
class A
{
public:
//friend class B;
typedef std::shared_ptr<A> APtr;
A() : next_() {}
A(APtr n) : next_(n) {}
APtr next() { return next_; }
void setNext(APtr n) { next_ = n;}
virtual void doIt() { std::cout<<"normal thing"<<std::endl; }
virtual ~A(){}
private:
APtr next_;
};
class B : public std::enable_shared_from_this<A>, private A // B really is a special A
// that should have different behavior
// at the tail of the chain
// but I want to hide A's interface
// to external clients
{
public:
template<class X> friend class std::enable_shared_from_this;
typedef std::shared_ptr<B> BPtr;
static BPtr makeit(A::APtr prev){
BPtr B(std::make_shared<B>());
prev->setNext(B->shared_from_this());
return B;
}
void doIt() {std::cout<<"end thing"<<std::endl;}
private:
B(){}
};
int main()
{
A::APtr dummyPtr;
A::APtr head = std::make_shared<A>(dummyPtr);
B::BPtr tail = B::makeit(head);
for(A::APtr curr = head; curr; curr=curr->next()){
curr->doIt();
}
return 0;
}
You need to use enable_shared_from_this because otherwise you're trying to create two 'families' of shared_ptr and that won't work.
I've made a factory method because fixing the constructor just wouldn't work!
There is a precondition of enable_shared_from_this that there has to be a std::shared_ptr in existence and I suppose that means 'fully constructed'.
The following constructor will not work for me:
B(A::APtr prev){
prev->setNext(shared_from_this());
}
That said, if you do inherit from enable_shared_from_this it's a good idea to make all the constructors private and provide factories that return shared_ptr. Otherwise you can get in a right mess if calling code doesn't itself ensure that 'pre-existing shared_ptr' condition. A nasty piece of coupling if ever there was one.
When you use private inheritance, you're basically saying "I want B to be implemented in terms of A, but I don't want it to be used like a A (is-a A)"
Here, you're giving boost::shared_ptr a pointer to B as if it was an A.
That's a contradiction in your design. Maybe declaring boost::shared_ptr<A> a friend of B would help, but it's still a weird design.
Additional note: If you want B to be a special A that doesn't expose A's interface, consider composition instead of private inheritance
I've read an "Item" about shared_ptr in Scott Meyers' book "Effective Modern C++" where he says the following:
The usual control block implementation is more
sophisticated than you might expect. It makes use of inheritance, and there’s even a virtual function. (It’s used to ensure that the pointed-to object is properly destroyed.)
That means that using std::shared_ptrs also incurs the cost of the machinery for the virtual function used by the control block.
Then he doesn't explain what a virtual function does exactly. As far as I know, the proper way of deleting a pointed object is using deleters or type erasure. So, please explain what this is about.
The virtual function is required to ensure the object being shared is appropriately deleted. Unlike a unique_ptr, shared_ptr does not require full knowledge of the type when its template is instantiated. E.g. you can do this:
class Foo;
std::shared_ptr<Foo> foo = make_foo();
Note that in the code above we don't have the complete Foo type, just the forward declaration. If we let foo go out of scope, the object it is pointing to will be correctly deleted because when Foo was created in make_foo, a deleter was also created which knows the complete type of Foo, and hence can call the appropriate destructors. (E.g. Perhaps make_foo creates a Bar that inherits from Foo and returns that. shared_ptr's will handle this fine.)
The function on the deleter object that shared_ptr creates to manage the deletion of Foo will be virtual, allowing shared_ptr to call the correct destructor.
Roughly this could be something like this:
struct deleter_interface {
virtual void ~deleter_interface = default;
virtual void dispose() = 0;
};
template <typename T>
struct deleter : deleter_interface {
T* ptr_;
deleter(T* ptr) : ptr_(ptr) {}
virtual void dispose() { delete ptr_; }
};
template <typename T>
shared_ptr {
T* ptr_;
deleter_interface* deleter_;
...
};
template <typename T>
shared_ptr<T>::shared_ptr<T>(T* ptr)
: ptr_(ptr), deleter_(new deleter<T>(ptr)) {}
template <typename T>
shared_ptr<T>::~shared_ptr<T>() { deleter_->dispose(); delete deleter_; }
While this seems more complicated that is strictly necessary, it is to allow shared_ptr to be used without the complete type. For example what if you wanted to do this:
In a.h:
struct Foo;
std::shared_ptr<Foo> a = make_object();
// ... let a go out of scope
And in a.cc:
struct Foo { ... };
struct Bar : Foo { ... };
std::shared_ptr<Foo> make_object() { return std::shared_ptr<Foo>(new Bar); }
If we didn't have the virtual function used in the deleter code, then Bar would not be destructed correctly. With the virtual function it doesn't matter that the header (a.h) never sees the definition of Foo or Bar.
I was reading the latest Overload (link) and decided to test out the statement at page 8:
shared_ptr will properly invoke B’s destructor on scope exit, even
though the destructor of A is not virtual.
I am using Visual Studio 2013, compiler v120:
#include <memory>
#include <iostream>
struct A {
~A() { std::cout << "Deleting A"; }
};
struct B : public A
{
~B() { std::cout << "Deleting B"; }
};
int main()
{
std::shared_ptr<A> ptr = std::make_shared<B>();
ptr.reset();
return 0;
}
This works as expected and prints out "Deleting BDeleting A"
The article seems to imply that this should also work with std::unique_ptr:
There is almost no need to manage your own resources so resist the
temptation to implement your own copy/assign/move construct/move
assign/destructor functions.
Managed resources can be resources inside
your class definition or instances of your classes themselves.
Refactoring the code around standard containers and class templates
like unique_ptr or shared_ptr will make your code more readable and
maintainable.
However, when changing
std::shared_ptr<A> ptr = std::make_shared<B>();
to
std::unique_ptr<A> ptr = std::make_unique<B>();
the program will only output "Deleting A"
Did I misunderstand the article and the behavior is intended by the standard?
Is it a bug with the MSVC compiler?
shared_ptr and unique_ptr are different. make_shared will create a type erased deleter object on invocation while with unique_ptr the deleter is part of the type. Therefore the shared_ptr knows the real type when it invokes the deleter but the unique_ptr doesn't. This makes unique_ptr's much more efficient which is why it was implemented this way.
I find the article a bit misleading actually. I do not consider it to be good advice to expose copy constructors of a base class with virtual functions, sounds like a lot of slicing problems to me.
Consider the following situation:
struct A{
virtual void foo(){ std::cout << "base"; };
};
struct B : A{
virtual void foo(){ std::cout << "derived"; };
};
void bar(A& a){
a.foo(); //derived
auto localA = a; //poor matanance porgrammer didn't notice that a is polymorphic
localA.foo(); //base
}
I would personally advocate non-intrusive polymorphism http://isocpp.org/blog/2012/12/value-semantics-and-concepts-based-polymorphism-sean-parent for any new higherarchies, it sidesteps the problem entirely.
This behaviour is according to the standard.
The unique_ptr is designed to have no performance overhead compared to the ordinary new/delete.
The shared_ptr has the allowance to have the overhead, so it can be smarter.
According to the standard [20.7.1.2.2, 20.7.2.2.2], the unique_ptr calls delete on the pointer returned by get(), while the shared_ptr deletes the real object it holds - it remembers the proper type to delete (if properly initialized), even without a virtual destructor.
Obviously, the shared_ptr is not all-knowing, you can trick it to behave badly by passing a pointer to the base object like this:
std::shared_ptr<Base> c = std::shared_ptr<Base> { (Base*) new Child() };
But, that would be a silly thing to do anyhow.
You could do something similar with unique_ptr, but since its deleter type is determined statically, you need to statically maintain the proper deleter type. i.e. (Live demo at Coliru):
// Convert given pointer type to T and delete.
template <typename T>
struct deleter {
template <typename U>
void operator () (U* ptr) const {
if (ptr) {
delete static_cast<T*>(ptr);
}
}
};
// Create a unique_ptr with statically encoded deleter type.
template <typename T, typename...Args>
inline std::unique_ptr<T, deleter<T>>
make_unique_with_deleter(Args&&...args) {
return std::unique_ptr<T, deleter<T>>{
new T(std::forward<Args>(args)...)
};
}
// Convert a unique_ptr with statically encoded deleter to
// a pointer to different type while maintaining the
// statically encoded deleter.
template <typename T, typename U, typename W>
inline std::unique_ptr<T, deleter<W>>
unique_with_deleter_cast(std::unique_ptr<U, deleter<W>> ptr) {
T* t_ptr{ptr.release()};
return std::unique_ptr<T, deleter<W>>{t_ptr};
}
// Create a unique_ptr to T with statically encoded
// deleter for type U.
template <typename T, typename U, typename...Args>
inline std::unique_ptr<T, deleter<U>>
make_unique_with_deleter(Args&&...args) {
return unique_with_deleter_cast<T>(
make_unique_with_deleter<U>(std::forward<Args>(args)...)
);
}
It's a bit awkward to use:
std::unique_ptr<A, deleter<B>> foo = make_unique_with_deleter<A, B>();
auto improves the situation:
auto bar = make_unique_with_deleter<A, B>();
It doesn't really buy you much, since the dynamic type is encoded right there in the static type of the unique_ptr. If you're going to carry the dynamic type around, why not simply use unique_ptr<dynamic_type>? I conjecture such a thing may have some use in generic code, but finding an example of such is left as an exercise to the reader.
The technique std::shared_ptr employs to do the magic is called type erasure. If you are using gcc, try to find the file bits/shared_ptr_base.h and check the implementation. I'm using gcc 4.7.2.
unique_ptr is designed for minimum overhead and does not use type erasure to remember the actual type of the pointer it holds.
Here is a great discussion on this topic: link
EDIT: a simple implementation of shared_ptr to showcase how type erasure is achieved.
#include <cstddef>
// A base class which does not know the type of the pointer tracking
class ref_counter_base
{
public:
ref_counter_base() : counter_(1) {}
virtual ~ref_counter_base() {}
void increase()
{
++counter_;
}
void release()
{
if (--counter_ == 0) {
destroy();
delete this;
}
}
virtual void destroy() = 0;
private:
std::size_t counter_;
};
// The derived class that actually remembers the type of
// the pointer and deletes the pointer on destroy.
template <typename T>
class ref_counter : public ref_counter_base
{
public:
ref_counter(T *p) : p_(p) {}
virtual void destroy()
{
delete p_;
}
private:
T *p_;
};
template <typename T>
class shared_ptr
{
public:
shared_ptr(T *p)
: p_(p)
, counter_(new ref_counter<T>(p))
{
}
// Y* should be implicitely convertable to T*,
// i.e. Y is derived from T
template <typename Y>
shared_ptr(Y &other)
: p_(other.get())
, counter_(other.counter())
{
counter_->increase();
}
~shared_ptr()
{
counter_->release();
}
T* get() { return p_; }
ref_counter_base* counter() { return counter_; }
private:
T *p_;
ref_counter_base *counter_;
};
If I have the following 3 classes to hide the data type and store information,
do I need a virtual destructor? I was led to believe no, but now I am not sure.
I would rather not include it if possible for preformance reasons. Classes stripped down for example sake.
#include <memory>
class DarkHideInterface
{
public:
bool test;
};
template <typename T>
class DarkHideInterfaceImpl : public DarkHideInterface
{
public:
DarkHideInterfaceImpl (const T& t ) : _t(t) {}
private:
T _t;
};
class DarkHide
{
public:
template <class T> DarkHide (const T& t) : p_(new DarkHideInterfaceImpl<T>(t) ) { }
private:
std::auto_ptr<DarkHideInterface> p_;
};
With auto_ptr, I think you need the virtual destructor, since the delete will happen polymorphically - in other words, internally auto_ptr will ultimately call delete on the stored DarkHideInterface*. If DarkHideInterface doesn't have a virtual destructor and the pointer points to a DarkHideInterfaceImpl instance, then you get undefined behaviour.
Note that you wouldn't need a virtual destructor with shared_ptr as far as I know, because that remembers the type of the pointer with which it was constructed and calls delete on that pointer directly (search for 'virtual destructor' here to see what I mean: http://www.boost.org/doc/libs/1_47_0/libs/smart_ptr/shared_ptr.htm).