I am reading through implementing smart pointers and I found the following code,
template <class T>
class SmartPtr
{
public:
explicit SmartPtr(T* pointee) : pointee_(pointee);
SmartPtr& operator=(const SmartPtr& other);
~SmartPtr();
T& operator*() const
{
...
return *pointee_;
}
T* operator->() const
{
...
return pointee_;
}
private:
T* pointee_;
...
};
I am not able to understand the following,
"SmartPtr& operator=(const SmartPtr& other)": why the parameter is constant? Doesn't it lose its ownership when the assignment is done?
And why do we need "T& operator*() const" and "T* operator->() const" methods?
Thx#
Point 1. Not necessarily, depends on the design of the smart pointer. Some like boost:shared_ptr do not transfer ownership on assignment.
Point 2. Those methods simulate normal pointer operations on the smart pointer.
To answer 2.:
To simulate a raw pointer. You can use *ptr to return the object it points to (just like a C-pointer), and you can use ptr->foo() to call the method foo in T, (just like a C-pointer).
There are two types of smart pointer semantics I can think of that would work with that signature. The first would be a reference counting pointer like std::shared_ptr. The other would be value semantics, i.e. copying the pointer makes a new copy of the pointed to object. This signature wouldn't work with a pointer like auto_ptr/unique_ptr.
Related
Let's say I want to represent a binary tree in C++. Usually, I want a Node struct like this:
struct Node {
Node* left
Node* right;
};
(Here I use struct and raw pointers just for simplicity. I know I should use smart pointers for memory management.)
This representation has a problem: I can never have a deep-const tree. (Correct me if I can.) I may mark a single Node const, but its children is hard-coded as non-const in the Node struct.
(I may use some template hack to make left and right optionally const, but this makes the const Node and non-const Node incompatible.)
Soon I found out, if I magically had some deep-const pointer (say deep_const_pointer, which makes constness transitive), I can use that pointer in Node so that having a const node automatically means having a const sub-tree.
I tried to write a deep-const pointer class, and here is what I end up with:
template <typename T>
class deep_const_pointer {
public:
explicit deep_const_pointer(const T* p_)
: p{const_cast<T*>(p_)} {}
const T& operator*() const { return *p; }
T& operator*() { return *p; }
const T* operator->() const { return p; }
T* operator->() { return p; }
// etc.
private:
T* p;
};
Here I cast out the const in the constructor and optionally add it back according to the constness of this pointer-like object. However, this implementation allows the following:
const int i = 42;
deep_const_pointer<int> p{&i};
*p = 0; // Oops!
So it depends on the user to correctly mark whether the pointer is const or not.
How should I build a deep-const pointer class? Ideally, I want the const check happen at compile-time, and that pointer class takes as much memory as a raw pointer. (Which rules out the solution to save the constness to a bool member variable and check on each access.)
EDIT: I checked std::experimental::propagate_const, and it is indeed not a "deep-const" pointer from my perspective. What I meant by deep-const pointer P is:
Constant P is pointer-to-const;
Mutable P is pointer-to-mutable;
A const reference to a non-const P is treated as if it were a const P;
Since pointer-to-const has value semantics, a const P should be trivially copyable.
propagate_const fails to match the requirement because:
It never accepts a pointer-to-const;
It is not copyable (copy constructors explicitly deleted);
From the comments and answer I received, I guess such a P is not implementable in C++.
Writing a transitive-const smart pointer is a solved problem, just look up std::experimental::propagate_const<>. It shouldn't be too hard to find appropriate implementations.
In your own try, you got constructing from a raw pointer wrong. You should not add const to the pointee-type, nor strip it out with a cast.
Fixed:
explicit deep_const_pointer(T* p_)
: p{p_} {}
if an object is already reference-counted (like glib in C), having obj_ref, obj_unref. All we have is a pointer like obj *p.
How can we use c++'s shared_ptr to manage the object so that we can have an uniform interface.
Ok, it seems that a lot of people have misunderstood my intension.
The greatest issue here is not about deleter. It's about inform of the original manager that I increased the refcount.
If I assigned or copied, only std::shared_ptr increased the refcount, but the original one did not. Is there anyway to inform it? So as the unref operation.
std::shared_ptr allows you to pass a custom deleter which is called when the owned object should be destroyed. You could use it to call obj_unref.
obj* p = create_obj();
p->obj_ref();
std::shared_ptr<obj> sp(p, [](auto p) {
p->obj_unref();
});
/* use sp normally, obj will be 'obj_unref'ed and deleted when sp goes out of scope */
I don't know how a obj is created and if it gets destroyed by obj_unref() when the count reaches 0, but I hope you see what I mean.
The idea is to increment objs internal reference count just once at the beginning, and decrement it just once when the last shared_ptr is destroyed.
Don't try to somehow duct tape std::shared_ptr's refcounting to your custom one, that won't end well. Just write a custom pointer:
struct objPtr {
objPtr()
: _ptr{nullptr} { }
objPtr(obj *ptr)
: _ptr{ptr} {
if(_ptr)
_ptr->obj_ref();
}
~objPtr() {
if(_ptr)
_ptr->obj_unref();
}
objPtr(objPtr const &orig)
: objPtr{orig._ptr} { }
objPtr &operator = (objPtr const &orig) {
obj *const oPtr = std::exchange(_ptr, orig._ptr);
_ptr->obj_ref();
oPtr->obj_unref();
return *this;
}
obj &operator * () { return *_ptr; }
obj const &operator * () const { return *_ptr; }
obj *operator -> () { return _ptr; }
obj const *operator -> () const { return _ptr; }
operator bool() const { return _ptr; }
bool operator ! () const { return !_ptr; }
private:
obj *_ptr;
};
Add move construction and assignment if you so wish.
When you want a shared_ptr, start with a unique_ptr. Then build up.
struct cleanup_obj {
// not called with nullptr:
void operator()(obj* t)const {
obj_unref(t);
}
};
using obj_unique_ptr = std::unique_ptr<T, cleanup_obj>;
using obj_shared_ptr = std::shared_ptr<T>;
template<class T>
obj_unique_ptr<T> make_unique_refcount( T* t ) {
using ptr=obj_unique_ptr<T>;
if (!t) return ptr();
obj_ref(t);
return ptr(t);
}
template<class T>
obj_shared_ptr<T> make_shared_refcount( T* t ) {
return make_unique_refcount(t); // implicit convert does right thing
}
What did I do?
First, I wrote a unique_ptr wrapper, because we may as well be complete, and it solves the shared_ptr case via the unique_ptr->shared_ptr implicit conversion.
For unique_ptr, we have to say we aren't using the default object destroyer. In this case, we are using a stateless function object that knows how to obj_unref an obj*. The stateless function object keeps the overhead at zero.
For the null case, we don't first add a reference, as that is rude.
For shared_ptr, the fact that we have a working unique_ptr makes it a free function. shared_ptr will happily store the destroyer function that unique_ptr has. It doesn't have to be told it has a special object destroyer, because shared_ptr type erases object destruction by default. (This is because unique_ptr<T> is zero-overhead over a naked pointer, while shared_ptr<T> has unavoidable overhead of the reference counting block; the designers figured once you have that reference counting block, adding in a type-erased destruction function was not really expensive).
Note that our obj_unique_ptr<T> is also zero overhead over a naked pointer. Quite often you'll want one of these instead of the shared one.
Now, you can upgrade the obj_unique_ptr to a full on intrusive pointer, with less overhead than a shared_ptr, if you want.
template<class T>
struct obj_refcount_ptr : obj_unique_ptr<T> // public
{
// from unique ptr:
obj_refcount_ptr(obj_unique_ptr<T> p):obj_unique_ptr<T>(std::move(p)){}
obj_refcount_ptr& operator=(obj_unique_ptr<T> p){
static_cast<obj_unique_ptr<T>&>(*this)=std::move(p);
return *this;
}
obj_refcount_ptr(obj_refcount_ptr&&)=default;
obj_refcount_ptr& operator=(obj_refcount_ptr&&)=default;
obj_refcount_ptr()=default;
obj_refcount_ptr(obj_refcount_ptr const& o):
obj_refcount_ptr(make_unique_refcount(o.get())
{}
obj_refcount_ptr& operator=(obj_refcount_ptr const& o) {
*this = make_unique_refcount(o.get());
return *this;
}
};
which I think covers it. Now it is a zero-overhead reference counting intrusive smart pointer. These intrusive smart pointers can be converted toa std::shared_ptr<T> via implicit conversion, as they are still unique_ptrs. They are just unique_ptrs we have taught to copy themselves!
It does require moving from an obj_refcount_ptr to get a shared_ptr. We can fix this:
operator std::shared_ptr<T>() const {
return obj_refcount_ptr(*this);
}
which creatres an obj_refcount_ptr copy of *this and moves it into the shared_ptr. Only one add ref is called, and the remove ref is only called when the shared_ptr count goes to zero.
The general approach is to start with the simplest smart pointer (unique_ptr), get it right, then exploit its implementation to get us the shared_ptr and eventually the refcount_ptr. We can test the unique_ptr implementation in isolation, and its correctness makes testing the richer pointers easier.
The most simplest approach, the least invasive one with the minimal possibility of breaking something, is to simply write your own facade for the object, with the underlying object as a private member and providing simple wrappers to access it.
Then use a std::shared_ptr to that.
It's an incredibly bad idea to have the same objects in multiple smart pointer implementations as their ref counts can't know about each other. As soon as the ref count hits zero in one it will delete the object even if the other still holds refs.
If you really had to you could construct your smart pointers with custom deleters (that do nothing), but I really wouldn't recommend this approach.
Pick one implementation and stick to it.
This question already has answers here:
std::unique_ptr and pointer to pointer
(2 answers)
Closed 7 years ago.
Say, I've got a library which initializes an object like this:
Type *object;
lib_init(&object); // lib_init takes Type **object as the parameter
So, what if I want to use the library with my code which uses smart pointers?
Is this a proper way?
Type *pObject;
lib_init(&pObject);
unique_ptr<Type> object(nullptr);
object.reset(pObject);
Or is there a smart way to do this?
You could wrap the initialization into a function that returns a unique pointer:
std::unique_ptr<Type> init_type()
{
Type* p;
lib_init(&p);
return std::unique_ptr<Type>(p);
}
Then
std::unique_ptr<Type> object(init_type());
Bear in mind that, unless the pointer is to be de-allocated with delete, you'll need to provide a custom deleter too. For example, assuming the de-allocation function is void lib_dispose(Type*), then
std::unique_ptr<Type, void (*)(Type*)> init_type()
{
Type* p;
lib_init(&p);
return std::unique_ptr<Type, void (*)(Type*)>(p, lib_dispose);
}
I don't think you need smart pointer here, you can take the standard library approach for std::string/std::vector etc. - wrap the pointer in RAII mechanism
class CType{
Type* m_Ptr;
public:
CType(){
lib_init(&m_Ptr);
}
~Ctype(){
lib_dispose(&m_Ptr);
}
//TODO: Copy constructor, move constructor assignment operators..
operator Type&(){
return *m_Ptr;
}
operator const Type&() const{
return *m_Ptr;
}
operator Type*(){
return m_Ptr;
}
operator const Type*() const{
return m_Ptr;
}
Type* asPointer(){
return m_Ptr;
}
};
now use Ctype anywhere you would use regular Type, the polymorphism will kick in when you try to use CType as Type* or Type&..
template <class T>
class deep_const_ptr
{
T * priv;
public:
deep_const_ptr(const deep_const_ptr & p_other); // copy ctor
T const * operator->() const;
T * operator->();
..... // other crap
};
void Cheese::doThings(const deep_const_ptr<Cheese> p_other)
{
deep_const_ptr<Cheese> awayGoesTheConst(p_other); // I don't want this to work.
awayGoesTheConst->doTerribleThings();
}
I'd like to block copy construction from a const to a non-const.
Maybe this doesn't even make any sense?
Might this be possible with the new C++11 / 14 / 17?
EDIT:
Yes, the issue is that if I don't give the copy constructor, STL containers wont work. And I'd quite need them to work.
EDIT:
deep_const_ptr was the solution what I came up with when faced with the problem of STL containers and const objects.
class Shape
{
private:
std::list<Vertex *> m_vertices;
public:
virtual void std::list<Vertex*> * const getVertices() { return &m_vertices; }
virtual void std::list<Vertex const *> * const getVertices() const { return &m_vertices; } // doesn't work, cannot convert between different types of lists
}
I want to provide Immutable Vertex objects from an Immutable Shape.
The two solutions I came up with were custom deep_const_ptr pointer types and custom lists where
Vertex const *& operator[](unsigned int) const;
I assume what you are after is a deep-const-pointer implementation and what to do with its copying. As you already found out, a simple copy constructor will not do, since you will just lose the const on your pointer. There is no other way to disallow the const removal on the outer object, so you just need to disable the copy constructor.
Now the current version of your question does not make it clear why that is a problem. From your comments and the previous question, I can only infer that you want to use the pointer in containers, std::vector<> specifically. The key to making that work is to disable copy, but enable move (Note that you need rvalue-reference support for this, which is part of C++11):
template <class T>
class deep_const_ptr
{
T * priv;
public:
deep_const_ptr(const deep_const_ptr &) = delete; // Do not allow this!
deep_const_ptr& operator=(const deep_const_ptr &) = delete; // Nor this!
deep_const_ptr(deep_const_ptr &&) = default; // But this is okay
deep_const_ptr& operator=(deep_const_ptr &&) = default; // So is this!
T const * operator->() const;
T * operator->();
..... // other crap
};
Containers typically only need move, not copy, to work.
If you really need support for the copy, you need to do it to a different type that only supports the const access. For example, you could specialize the deep_const_ptr for const T and only implement the const variant of operator-> and operator* in there. Additionally, you'd allow "demotion" of a deep_const_ptr to a deep_const_ptr via the conversion operator. But this implies some sort of multiple owner handling, such as reference counting, so I'm going to leave it out. It's also weird that the const and non-const version behave differently, but it's your decision. Personally, I'd recommend just sticking with the move.
Edit Edit: You updated your answer again, so let me reply to that.
Just use a const pointer:
void Cheese::doThings(const Cheese* p_other)
{
Cheese* p = p_other; // compiler ERROR
p->doTerribleThings(); // can not do terrible things
}
Or if you want a shared_ptr:
void Cheese::doThings(const std::shared_ptr<const Cheese>& p_other)
{
std::shared_ptr<Cheese> p = p_other; // compiler ERROR
p->doTerribleThings(); // can not do terrible things
}
const ptr<T> is not the same as ptr<const T>. The first indicates a pointer object which can not be changed, while the object pointed to can. The second indicates a pointer object which can be changed, but the object pointed to can not.
For a function signature void foo(const A a) does not make much sense. You say "give me a copy, and I promise to not change it". Who cares what you do with your copy? Use void foo(const A& a) to avoid creating a copy.
Edit:
I'd like to allow copying nonconst -> nonconst, nonconst -> const,
const -> const.
This is already given for a "normal" pointer T* or an std::shared_ptr.
Old:
Just use T* operator->() const;. If the stored pointer will be a non-const pointer like Cheese* it can be converted to a const Cheese* automatically.
The function operator->() is marked as const, as it does not change the pointer object itself. This has nothing to do with the stored object!
Example with vector:
std::vector<Cheese*> a(10);
a[0] = 0; // does change a
Cheese* p = a[0]; // does not change a
a[0]->foo(); // still does not change a!
If you want objects pointed to by deep_const_ptr to be const, you have to use deep_const_ptr<const Cheese>.
Example with vector:
std::vector<const Cheese*> a(10); // const pointer!
a[0] = 0; // does change a
const Cheese* p = a[0]; // does not change a
a[0]->foo(); // now this only works if Cheese::foo is const
I'm following an example in Accelerated C++ and writing a simple Handle class that will act as a smart pointer. This uses the virtual ctor idiom using a virtual clone() function. So far so good. But what to do when I want to use my Handle for classes that I don't control that don't provide clone()?
The method suggested in the book is to create a global clone function and use template specialization (something I'm seeing for the first time) so that if clone() is called with a particular argument, one can write code to handle that case.
My question is: This means that I have to write a clone() version for every type of class that I envision my user can use Handle with. This seems quite hard! Is there a more elegant and/or simple way to solve this issue? How is it possible that things like auto_ptr or boost::shared_ptr are able to provide this functionality without the tedious clone() definitions?
For completeness, here's my Handle class implementation:
template <class T> class Handle
{
public:
Handle() : p(0) {}
Handle(T* t) : p(t) {}
Handle( const Handle& s ) :p(0) { if (s.p) p = s.p->clone(); }
const Handle& operator=( const Handle& );
~Handle() { delete p; }
operator bool() { return p; }
T& operator*() { if (p) return *p; else throw std::runtime_error("Handle not bound"); }
T* operator->() { if (p) return p; else throw std::runtime_error("Handle not bound"); }
private:
T* p;
};
Thanks!
The solution to this problem is to simply not write Handles for these kinds of classes. No. Really.
auto_ptr (deprecated as of C++11) never needs to clone the underlying object, because auto_ptr never copies the object. An auto_ptr only ever has one copy of the object, and when the auto_ptr is copied, control of the object is transfered -- that object is not copied.
unique_ptr never needs to clone the underlying object, because there is only ever one unique_ptr that owns the object. unique_ptr is noncopyable, and is only movable.
shared_ptr never needs to clone because it also only ever controls one copy of the object. Copying the shared_ptr only increments the reference count, and that single object is destroyed when the reference count is zero.
In general, if there's no way to deep copy the resource your class is controlling, then you should just make the class noncopyable. If clients need to pass references to your class around, they can place the class in an auto_ptr, unique_ptr, or shared_ptr themselves.