I'm trying to realize a smart pointer by myself. I know that I can use smart pointers instead of this but I'm trying to do this only for understanding smart pointers structure.
Тhe problem is the following when my smart pointer has been starting to call the destructor, this is checking if my pointer is not nullptr then if it is true this will delete ptr.
After this when the destructor has been calling again for CastS I'm getting an exception because the destructor is trying to delete an already deleted element and my if statement for the second time isn't working (as I was expecting) because after the deletion of an element the address is changing and the pointer isn't null anymore.
How Can I improve this code and how can I not delete twice already deleted pointer?
#include <iostream>
#include <string>
#include <chrono>
#include <thread>
#include <memory>
using std::cout;
using std::endl;
template<typename T>
class Smart_Pointer
{
private:
T* ptr;
public:
Smart_Pointer(T* ptr);
~Smart_Pointer();
T& operator*();
};
template<typename T>
Smart_Pointer<T>::Smart_Pointer(T* ptr)
{
this->ptr = ptr;
}
template<typename T>
Smart_Pointer<T>::~Smart_Pointer()
{
if (ptr != nullptr)
{
delete ptr;
ptr = nullptr;
}
}
template<typename T>
T& Smart_Pointer<T>::operator*()
{
return *ptr;
}
int main()
{
Smart_Pointer<int> castS(new int(10));
Smart_Pointer<int> castS2 = castS;
}
Smart Pointer is an umbrella term. For the std library it describes unique_ptr, shared_ptr and weak_ptr.
If you want to implement a unique ptr you need to ensure that only one
unique ptr owns the raw pointer, so you need to delete the copy constructor and copy assignment operator. To be able to transfer ownership between your unique pointers you have to provide move constructor and move assignment operator in a way that the ownership is transferred.
If you want to implement a shared ptr you need to implement reference counting.
How Can I delete all pointers to the same address? C++
That's something you don't want to do, you want to keep the managed object alive as long as there is at least one shared pointer owning that managed object.
The rule of three/five/zero:
Rule of three: If a class requires a user-defined destructor, a user-defined copy constructor, or a user-defined copy assignment operator, it almost certainly requires all three.
Rule of five: Because the presence of a user-defined destructor, copy-constructor, or copy-assignment operator prevents implicit definition of the move constructor and the move assignment operator, any class for which move semantics are desirable, has to declare all five special member functions:
How Can I delete all pointers to the same address? C++
You probably cannot do that reliably and automatically. Be aware of Rice's theorem, and read more about programming in C++, then see this C++ reference. Understand that pointers are organizing your virtual address space as some directed graph which evolves during the execution of your program.
Maybe you want to clear all pointers to the same address.
Then, read more about Garbage Collection.
For example, read the GC handbook.
Consider using some static analysis tool on your C++ source code, e.g. the Clang static analyzer.
Consider also generating some your C++ code (like SWIG or GNU bison does). You might code your generator of C++ code (e.g. using GPP or GNU m4, or your own generator of C++ files) to ease the management of your pointers.
Read also n3337 (some draft C++ standard) and the documentation of your C++ compiler (perhaps GCC).
Be aware that reference counting has drawbacks (e.g. it is not multi-thread friendly).
Study for inspiration the source code of existing C++ open source programs (e.g. on github), such as Fish, Qt, RefPerSys, GCC, Clang, ANTLR. Consider contributing to one of them.
If you are trying to emulate the behaviour of unique_ptr, this is solved by simply not allowing copy. If you delete the copy constructor, no two smart pointers can be pointing to the same address at the same time. You simply write:
SmartPtr(const SmartPtr&) = delete;
However, if you do this you might want a way to transfer ownership, so it would be nice to implement a move constructor:
SmartPtr(SmartPtr&& other) {
ptr = std::exchange(other.ptr, nullptr);
}
Or something like that. If you really want to allow two smart pointers to point to the same address, you need a way to decide which one is going to delete the ptr, usually the last one that goes out of scope. The way this is done in the standard (eg. In shared_ptr) is by defining a shared structure between all instances of the class, but that's probably way out of the scope of this answer..
How Can I delete all pointers to the same address?
First problem is that you don't know how many points to the same address.
This can be solved in many ways.
Using a control block (like std::shared_ptr) all smart pointers point to a control block that points to the real object and has a counter.
Using an intrusive counter in the pointer to object, either inherit it or the object must be inherited by the counter.
Using an external free counter.
Analyse all memory to see if anything points too that address (Garbage collection, GC).
The counter should be atomic if you intent to do multi-threading.
Implementing an external free counter has the huge advantage that you don't have the indirection of 1. and don't have the intrusiveness of 2 nor the complexity of GC.
Untested code
std::unordered_map<void *, std::atomic_int> extro_count;
You then need to implement the rule of 3 or rule of 5 depending on how advanced you want to make it.
template<typename T>
Smart_Pointer<T>::~Smart_Pointer() {
if (ptr != nullptr) {
if (--extro_count[ptr] == 0) {
extro_count.erase(ptr);
delete ptr;
ptr = nullptr;
}
}
I leave the implementation of the others to the OP.
This is still very unsafe in threaded code unless your careful.
Related
Shared pointers are good idea, no doubt. But as long as a large scale program includes raw pointers, I think there is a big risk in using shared pointers. Mainly, you will loose control of the real life-cycle of pointers to objects that hold raw pointers, and bugs will occur in locations which are more difficult to find and debug.
So my question is, was there no attempt to add to modern c++ a "weak pointer" which does not depend on using shared pointers? I mean just a pointer which becomes NULL when deleted in any part of the program. Is there a reason not to use such a self-made wrapper?
To better explain what I mean, the following is such a "weak pointer" that I made. I named it WatchedPtr.
#include <memory>
#include <iostream>
template <typename T>
class WatchedPtr {
public:
// the only way to allocate new pointer
template <typename... ARGS>
WatchedPtr(ARGS... args) : _ptr (new T(args...)), _allocated (std::make_shared<bool>(true)) {}
WatchedPtr(const WatchedPtr<T>& other) : _ptr (other._ptr), _allocated (other._allocated) {}
// delete the pointer
void del () {delete _ptr; *_allocated = false;}
auto& operator=(const WatchedPtr<T> &other) { return *this = other; }
bool isNull() const { return *_allocated; }
T* operator->() const { return _ptr; }
T& operator*() const { return *_ptr; }
private:
T* _ptr;
std::shared_ptr <bool> _allocated;
};
struct S {
int a = 1;
};
int main () {
WatchedPtr<S> p1;
WatchedPtr<S> p2(p1);
p1->a = 8;
std::cout << p1.isNull () << std::endl;
std::cout << p2.isNull () << std::endl;
p2.del ();
std::cout << p1.isNull () << std::endl;
std::cout << p1.isNull () << std::endl;
return 0;
}
Result:
1
1
0
0
-Edited-
Thank you all. Some clarifications following the comments and answers so far:
The implementation I presented for WatchedPtr is merely to demonstrate what I mean: a pointer that does not get the copy from external allocation, cannot be deleted externally, and becomes null if it is deleted. The implementation is knowingly far from perfect and was not meant to be perfect.
Problem with mix of shared_ptr and raw pointers is very common: A* is held as raw pointer, thus created at some point of the program and explicitly deleted at some point of the program. B holds a A*, and B* is held as shared_ptr, thus B* has vague lifespan. Thus B may live long after the deletion of A* that B holds.
The main usage of "WatchedPtr" in my mind is defensive programing. i.e. check for null and do the best thing possible for continuity (and a debug error). shared_ptr can do it, but in a very dangerous way - it will hide and delay the problem.
There can also be a design usage for "WatchedPtr" (very few and explicit "owners"), but this is not the main idea. For that indeed shared pointers are doing the job.
The intention of "WatchedPtr" is not for replacing all existing raw pointers in the program at once. It is not the same effort as replacing to shared_ptr, which IMHO has be done for the whole program at once. Which is unrealistic for large scale programs.
Weak pointers rely on notifications from the smart pointer infrastructure, so you could never do this with actual raw pointers.
One could imagine an extension of, say, unique_ptr which supported weak pointers, certainly. Presumably the main reason that nobody rushed in to implement such a feature is that weak pointers are already at the "Use refcounting and everything should just work" end of the scale, while unique_ptr is at the "Manage your lifetimes through RAII or you're not a real C++ programmer" end of the scale. Weak pointers also require there to be a separate control block per allocation, meaning that the performance advantage of such a WatchedPtr would be minimal compared to shared_ptr.
I think there is a big risk in using shared pointers. Mainly, you will loose control of the real life-cycle of pointers to objects that hold raw pointers, and bugs will occur in locations which are more difficult to find and debug.
Then you say
just a pointer which becomes NULL when deleted in any part of the program.
Don't you see the contradiction?
You don't want to use shared pointer because the lifetime of objects are determined at runtime. So far so good.
However, you want a pointer that automatically becomes null when the owner deletes it. The problem is if the lifetime of your pointer is known, you should not need that at all! If you know when the lifetime of your pointer ends, then you should be able to remove all instances of that pointer, a have a mean to check if the pointer is dead.
If you have a pointer that you don't know when the owner will free it and have no way to check or no observable side effect for the point of view of the weak owner, then do you really have control over lifetime of your pointer? Not really.
In fact, your implementation rely on containing a shared pointer. This is enlightening in the sense that you need some form of shared ownership in order to implement a raw pointer that can have weak pointer to it. Then if you need shared ownership to implement a raw pointer with weak references, you are left with a shared pointer. That's why the existence of your proposed class is contradictory.
std::shared_ptr + std::weak_ptr is made to deal with the issue of "parts of your program don't know when the owner free the resouce". What you need is a single std::shared_ptr and multiple std::weak_ptr, so they know when the resource is freed. These classes have the infomation needed to check the lifetime of a variable at runtime.
Or if in the contrary you know the lifetime of your pointers, then use that knowledge and find a way to remove dangling pointers, or expose a way to check for dangling pointers.
Reading the answers and comments, along with C++ Core Guidelines by Bjarne Stroustrup & Herb Sutter, I have come to the following answer:
When following the guidelines, there is no need for a "WatchedPtr" which involves "new" and "delete". However, a way to track the validity of a raw pointer taken from a smart pointer, is still in question for me, for debug/QA purposes.
In details:
Raw pointers should continue to be used. For various reasons. However, explicit "new" and "delete" should not. The cases of calling "new" and "delete" should all be replaced by shared_ptr/unique_ptr.
At the place where a raw pointer is currently allocated, there is no point in replacing it by "WatchedPtr".
If replacing a raw pointer to something else where it is allocated, it will be in most cases to unique_ptr, and on the other cases to shared_ptr. The "WatchedPtr", if at all, will continue from that point, built from the shared/unique pointer.
Therefor I have posted a somewhat different question.
I encountered a question when I was reading the item28 in More Effective C++. In this item, the author shows to us that we can use member template in SmartPtr such that the SmartPtr<Cassette> can be converted to SmartPtr<MusicProduct>.
The following code is not the same as in the book, but has the same effect.
#include <iostream>
class Base {};
class Derived : public Base {};
template<typename T>
class smart {
public:
smart(T* ptr)
: ptr(ptr)
{}
template<typename U>
operator smart<U>()
{
return smart<U>(ptr);
}
~smart()
{
delete ptr;
}
private:
T* ptr;
};
void test(const smart<Base>& ) {}
int main()
{
smart<Derived> sd(new Derived);
test(sd);
return 0;
}
It indeed can be compiled without compilation error. But when I ran the executable file, I got a core dump. I think that's because the member function of the conversion operator makes a temporary smart, which has a pointer to the same ptr in sd (its type is smart<Derived>). So the delete directive operates twice. What's more, after calling test, we can never use sd any more, since ptr in sd has already been delete.
Now my questions are :
Is my thought right? Or my code is not the same as the original code in the book?
If my thought is right, is there any method to do this?
Thanks very much for your help.
Yes, you've described the problem with your code fairly accurately.
As far as how to make it work: just about like the usual when you run into problems from a shallow copy: do a deep copy instead. That is, instead of just creating another pointer to the same data, you'd need to clone the data, and have the second object point to the clone of the data instead of the original data.
Alternatively, use a reference counted pointer, and increment the reference count when you do a copy, and decrement it when a copy is destroyed. When the count reaches zero (and not before) delete the pointee data.
Generally speaking: avoid doing all of this. Assuming you're using a relatively up-to-date compiler, the standard library should already contain a shared_ptr and a unique_ptr that can handle a lot of your smart pointer needs.
Your interpretation is correct, the conversion operator will create a different object that holds a pointer to the same underlying object. Once it goes out of scope it will be destroyed, and it will in turn call delete.
Not sure I understand the last question, if what you ask is whether this can be useful or not, it can be useful if implemented correctly. For example, if instead of a raw pointer and manually allocating/deleting the memory you were using a std::shared_ptr then it would work just fine. In other cases there might not even be a dynamically allocated object... This is just a tool, use it where it makes sense.
I need a collection in which i can store heap-allocated objects having virtual functions.
I known about boost::shared_ptr, std::unique_ptr (C++11) and boost::ptr_(vector|list|map), but they doesn't solve duplicate pointer problem.
Just to describe a problem - i have a function which accepts heap-allocated pointer and stores it for future use:
void SomeClass::add(T* ptr)
{
_list.push_back(ptr);
}
But if i call add twice with same parameter ptr - _list will contain two pointers to same object and when _list is destructed multiple deletion of same object will occur.
If _list will count pointer which he stores and uses them at deletion time then this problem will be solved and objects will not be deleted multiple times.
So the question is:
Does somebody knows some library with collections (vector,list,map in essence) of pointer with auto-delete on destruction and support of reference counting?
Or maybe i can solve this problem using some other technique?
Update:
I need support of duplicate pointers. So i can't use std::set.
As Kerrek SB and Grizzly mentioned - it is a bad idea to use raw pointers in general and suggests to use std::make_shared and forget about instantiation via new. But this is responsibility of client-side code - not the class which i designs. Even if i change add signature (and _list container of course) to
void SomeClass::add(std::shared_ptr<T> ptr)
{
_list.push_back(ptr);
}
then somebody (who doesn't know about std::make_shared) still can write this:
SomeClass instance;
T* ptr = new T();
instance.add(ptr);
instance.add(ptr);
So this is not a full solution which i wait, but useful if you write code alone.
Update 2:
As an alternative solution i found a clonning (using generated copy constructor). I mean that i can change my add function like this:
template <typename R>
void SomeClass::add(const R& ref)
{
_list.push_back(new R(ref));
}
this will allow virtual method (R - class which extends some base class (interface)) calls and disallow duplicate pointers. But this solution has an overhead for clone.
Yes: std::list<std::shared_ptr<T>>.
The shared pointer is avaiable from <memory>, or on older platforms from <tr1/memory>, or from Boost's <boost/shared_ptr.hpp>. You won't need to delete anything manually, as the shared pointer takes care of this itself. You will however need to keep all your heap pointers inside a shared pointer right from the start:
std::shared_ptr<T> p(new T); // legacy
auto p = std::make_shared<T>(); // better
If you another shared pointer to the same object, make a copy of the shared pointer (rather than construct a new shared pointer from the underlying raw pointer): auto q = p;
The moral here is: If you're using naked pointers, something is wrong.
Realize that smart pointers are compared by comparing the underlying container. So you can just use a std::set of whatever smartpointer you prefer. Personally I use std::unique_ptr over shared_ptr, whenever I can get away with it, since it makes the ownership much clearer (whoever holds the unique_ptris the owner) and has much lower overhead too. I have found that this is enough for almost all my code. The code would look something like the following:
std::set<std::unique_ptr<T> > _list;
void SomeClass::add(T* ptr)
{
std::unique_ptr<T> p(ptr);
auto iter = _list.find(p);
if(iter == _list.end())
_list.insert(std::move(p));
else
p.release();
}
I'm not sure right now if that is overkill (have to check if insert is guaranteed not to do anything, if the insertion fails), but it should work. Doing this with shared_ptr<T> would look similar, although be a bit more complex, due to the lack of a relase member. In that case I would probably first construct a shared_ptr<T> with a do nothing deleter too pass to the call to find and then another shared_ptr<T> which is actually inserted.
Of course personally I would avoid doing this and always pass around smart pointers when the ownership of a pointer changes hands. Therefore I would rewrite SomeClass::add as void SomeClass::add(std::unique_ptr<T> ptr) or void SomeClass::add(std::shared_ptr<T> ptr) which would pretty much solve the problem of having multiple instances anyways (as long as the pointer is always wrapped).
What are some ways you can shoot yourself in the foot when using boost::shared_ptr? In other words, what pitfalls do I have to avoid when I use boost::shared_ptr?
Cyclic references: a shared_ptr<> to something that has a shared_ptr<> to the original object. You can use weak_ptr<> to break this cycle, of course.
I add the following as an example of what I am talking about in the comments.
class node : public enable_shared_from_this<node> {
public :
void set_parent(shared_ptr<node> parent) { parent_ = parent; }
void add_child(shared_ptr<node> child) {
children_.push_back(child);
child->set_parent(shared_from_this());
}
void frob() {
do_frob();
if (parent_) parent_->frob();
}
private :
void do_frob();
shared_ptr<node> parent_;
vector< shared_ptr<node> > children_;
};
In this example, you have a tree of nodes, each of which holds a pointer to its parent. The frob() member function, for whatever reason, ripples upwards through the tree. (This is not entirely outlandish; some GUI frameworks work this way).
The problem is that, if you lose reference to the topmost node, then the topmost node still holds strong references to its children, and all its children also hold a strong reference to their parents. This means that there are circular references keeping all the instances from cleaning themselves up, while there is no way of actually reaching the tree from the code, this memory leaks.
class node : public enable_shared_from_this<node> {
public :
void set_parent(shared_ptr<node> parent) { parent_ = parent; }
void add_child(shared_ptr<node> child) {
children_.push_back(child);
child->set_parent(shared_from_this());
}
void frob() {
do_frob();
shared_ptr<node> parent = parent_.lock(); // Note: parent_.lock()
if (parent) parent->frob();
}
private :
void do_frob();
weak_ptr<node> parent_; // Note: now a weak_ptr<>
vector< shared_ptr<node> > children_;
};
Here, the parent node has been replaced by a weak pointer. It no longer has a say in the lifetime of the node to which it refers. Thus, if the topmost node goes out of scope as in the previous example, then while it holds strong references to its children, its children don't hold strong references to their parents. Thus there are no strong references to the object, and it cleans itself up. In turn, this causes the children to lose their one strong reference, which causes them to clean up, and so on. In short, this wont leak. And just by strategically replacing a shared_ptr<> with a weak_ptr<>.
Note: The above applies equally to std::shared_ptr<> and std::weak_ptr<> as it does to boost::shared_ptr<> and boost::weak_ptr<>.
Creating multiple unrelated shared_ptr's to the same object:
#include <stdio.h>
#include "boost/shared_ptr.hpp"
class foo
{
public:
foo() { printf( "foo()\n"); }
~foo() { printf( "~foo()\n"); }
};
typedef boost::shared_ptr<foo> pFoo_t;
void doSomething( pFoo_t p)
{
printf( "doing something...\n");
}
void doSomethingElse( pFoo_t p)
{
printf( "doing something else...\n");
}
int main() {
foo* pFoo = new foo;
doSomething( pFoo_t( pFoo));
doSomethingElse( pFoo_t( pFoo));
return 0;
}
Constructing an anonymous temporary shared pointer, for instance inside the arguments to a function call:
f(shared_ptr<Foo>(new Foo()), g());
This is because it is permissible for the new Foo() to be executed, then g() called, and g() to throw an exception, without the shared_ptr ever being set up, so the shared_ptr does not have a chance to clean up the Foo object.
Be careful making two pointers to the same object.
boost::shared_ptr<Base> b( new Derived() );
{
boost::shared_ptr<Derived> d( b.get() );
} // d goes out of scope here, deletes pointer
b->doSomething(); // crashes
instead use this
boost::shared_ptr<Base> b( new Derived() );
{
boost::shared_ptr<Derived> d =
boost::dynamic_pointer_cast<Derived,Base>( b );
} // d goes out of scope here, refcount--
b->doSomething(); // no crash
Also, any classes holding shared_ptrs should define copy constructors and assignment operators.
Don't try to use shared_from_this() in the constructor--it won't work. Instead create a static method to create the class and have it return a shared_ptr.
I've passed references to shared_ptrs without trouble. Just make sure it's copied before it's saved (i.e., no references as class members).
Here are two things to avoid:
Calling the get() function to get the raw pointer and use it after the pointed-to object goes out of scope.
Passing a reference of or a raw pointer to a shared_ptr should be dangerous too, since it won't increment the internal count which helps keep the object alive.
We debug several weeks strange behavior.
The reason was:
we passed 'this' to some thread workers instead of 'shared_from_this'.
Not precisely a footgun, but certainly a source of frustration until you wrap your head around how to do it the C++0x way: most of the predicates you know and love from <functional> don't play nicely with shared_ptr. Happily, std::tr1::mem_fn works with objects, pointers and shared_ptrs, replacing std::mem_fun, but if you want to use std::negate, std::not1, std::plus or any of those old friends with shared_ptr, be prepared to get cozy with std::tr1::bind and probably argument placeholders as well. In practice this is actually a lot more generic, since now you basically end up using bind for every function object adaptor, but it does take some getting used to if you're already familiar with the STL's convenience functions.
This DDJ article touches on the subject, with lots of example code. I also blogged about it a few years ago when I first had to figure out how to do it.
Using shared_ptr for really small objects (like char short) could be an overhead if you have a lot of small objects on heap but they are not really "shared". boost::shared_ptr allocates 16 bytes for every new reference count it creates on g++ 4.4.3 and VS2008 with Boost 1.42. std::tr1::shared_ptr allocates 20 bytes. Now if you have a million distinct shared_ptr<char> that means 20 million bytes of your memory are gone in holding just count=1. Not to mention the indirection costs and memory fragmentation. Try with the following on your favorite platform.
void * operator new (size_t size) {
std::cout << "size = " << size << std::endl;
void *ptr = malloc(size);
if(!ptr) throw std::bad_alloc();
return ptr;
}
void operator delete (void *p) {
free(p);
}
Giving out a shared_ptr< T > to this inside a class definition is also dangerous.
Use enabled_shared_from_this instead.
See the following post here
You need to be careful when you use shared_ptr in multithread code. It's then relatively easy to become into a case when couple of shared_ptrs, pointing to the same memory, is used by different threads.
The popular widespread use of shared_ptr will almost inevitably cause unwanted and unseen memory occupation.
Cyclic references are a well known cause and some of them can be indirect and difficult to spot especially in complex code that is worked on by more than one programmer; a programmer may decide than one object needs a reference to another as a quick fix and doesn't have time to examine all the code to see if he is closing a cycle. This hazard is hugely underestimated.
Less well understood is the problem of unreleased references. If an object is shared out to many shared_ptrs then it will not be destroyed until every one of them is zeroed or goes out of scope. It is very easy to overlook one of these references and end up with objects lurking unseen in memory that you thought you had finished with.
Although strictly speaking these are not memory leaks (it will all be released before the program exits) they are just as harmful and harder to detect.
These problems are the consequences of expedient false declarations: 1. Declaring what you really want to be single ownership as shared_ptr. scoped_ptr would be correct but then any other reference to that object will have to be a raw pointer, which could be left dangling. 2. Declaring what you really want to be a passive observing reference as shared_ptr. weak_ptr would be correct but then you have the hassle of converting it to share_ptr every time you want to use it.
I suspect that your project is a fine example of the kind of trouble that this practice can get you into.
If you have a memory intensive application you really need single ownership so that your design can explicitly control object lifetimes.
With single ownership opObject=NULL; will definitely delete the object and it will do it now.
With shared ownership spObject=NULL; ........who knows?......
If you have a registry of the shared objects (a list of all active instances, for example), the objects will never be freed. Solution: as in the case of circular dependency structures (see Kaz Dragon's answer), use weak_ptr as appropriate.
Smart pointers are not for everything, and raw pointers cannot be eliminated
Probably the worst danger is that since shared_ptr is a useful tool, people will start to put it every where. Since plain pointers can be misused, the same people will hunt raw pointers and try to replace them with strings, containers or smart pointers even when it makes no sense. Legitimate uses of raw pointers will become suspect. There will be a pointer police.
This is not only probably the worst danger, it may be the only serious danger. All the worst abuses of shared_ptr will be the direct consequence of the idea that smart pointers are superior to raw pointer (whatever that means), and that putting smart pointers everywhere will make C++ programming "safer".
Of course the mere fact that a smart pointer needs to be converted to a raw pointer to be used refutes this claim of the smart pointer cult, but the fact that the raw pointer access is "implicit" in operator*, operator-> (or explicit in get()), but not implicit in an implicit conversion, is enough to give the impression that this is not really a conversion, and that the raw pointer produced by this non-conversion is an harmless temporary.
C++ cannot be made a "safe language", and no useful subset of C++ is "safe"
Of course the pursuit of a safe subset ("safe" in the strict sense of "memory safe", as LISP, Haskell, Java...) of C++ is doomed to be endless and unsatisfying, as the safe subset of C++ is tiny and almost useless, as unsafe primitives are the rule rather than the exception. Strict memory safety in C++ would mean no pointers and only references with automatic storage class. But in a language where the programmer is trusted by definition, some people will insist on using some (in principle) idiot-proof "smart pointer", even where there is no other advantage over raw pointers that one specific way to screw the program state is avoided.
I need a smart pointer for my project which can be send to several methods as parameter. I have checked auto_ptr and shared_ptr from boost. But IMO, that is not suitable for my requirements. Following are my findings
auto_ptr : When passed to another method, ownership will be transferred and underlying pointer will get deleted when that method's scope ends. We can workaround this by passing auto_ptr by reference, but there is no compile time mechanism to ensure it is always passed by reference. If by mistake, user forgot to pass a reference, it will make problems.
boost::shared_ptr : This looks promising and works correctly for my need. But I feel this is overkill for my project as it is a very small one.
So I decided to write a trivial templated pointer container class which can't be copied by value and take care about deleting the underlying pointer. Here it is
template <typename T>
class simple_ptr{
public:
simple_ptr(T* t){
pointer = t;
}
~simple_ptr(){
delete pointer;
}
T* operator->(){
return pointer;
}
private:
T* pointer;
simple_ptr(const simple_ptr<T>& t);
};
Is this implementation correct? I have made copy constructor as private, so that compiler will alert when someone tries to pass it by value.
If by chance the pointer is deleted, delete operation on the destructor will throw assertion error. How can I workaround this?
I am pretty new to C++ and your suggestion are much appreciated.
Thanks
Please use boost::scoped_ptr<> as suggested by Martin York, because it:
Does exactly what you want (it's a noncopyable pointer)
Has no overhead above that of a standard C pointer
Has been carefully crafted by super-intelligent C++ wizards to make sure it behaves as expected.
While I can't see any problems with your implementation (after applying the changes suggested by ChrisW), C++ has many dark corners and I would not be surprised if there is some obscure corner case which you, I and the others here have failed to spot.
What you have done is boost::scoped_ptr
Please also read comment by j_random_hacker.
Is this implementation correct? I have made copy constructor as private ...
You could do the same for the assignment operator:
simple_ptr& operator=(const simple_ptr<T>& t);
A const version of the dereference operator might be useful too, and, smart pointers usually define the other kind of dereference operator as well:
const T* operator->() const { return pointer; }
const T& operator*() const { return *pointer; }
T& operator*() { return *pointer; }
If by chance the pointer is deleted, delete operation on the destructor will throw assertion error. How can I workaround this?
Do you mean, if I do this:
//create instance
Car* car = new Car;
//assign to smart ptr
simple_ptr<Car> ptr(car);
//explicit delete
delete car;
//... assertion when ptr is destroyed ...
A way (I don't know if it's a good way) to prevent that is to declare the constructor, and/or the destructor, and/or the delete operator of the T class as private, and say that simple_ptr is a friend of the T class (so that only the simple_ptr class can create and/or destroy and/or delete instances of T).
Marking the new operator as private in T class seems to be impossible as I have to modify all the classes which will be used with simple_ptr
Yes that's true: to do my suggestion immediately above, you would need to modify the class definitions.
If your question is "how can I make double deletes impossible, without modifying class definitions?" then I think the answers are:
You can't: it's up the application code (which uses these classes) to be careful
You can: by providing your own heap manager i.e. your own implementation of global operator new and global operator delete and, in your smart_ptr code, interrogate your heap manager to see whether this incarnation of this pointer is still allocated, before you delete it
To answer your first question, the best assurance you can get that this is correct is to implement a test harness around it to do some simple task, and make sure you get the behavior you expect. For me, that is far better comfort the code is right than the opinion of some random person reading it.
As for your second question, you work around delete throwing an assertion error by setting pointer to some marker value after you delete it the first time. Something like:
if (pointer) {
delete pointer;
pointer = NULL;
} else {
error("Attempted to free already freed pointer.");
}
The problem you're going to run into here is that your overloaded -> operator returns the value of the pointer, which means whoever you return this to can also call delete, causing the check I propose above not to work. For example:
simple_ptr<int> myPtr = someIntPointer;
...
delete myPtr.operator->(); /* poof goes your pointered memory region */
I might recommend that you not rely on the operator-> overloading, and just require that those using this class call a method that dereferences the pointer internally before passing that value back to the user.
Hope this helps.
You should user a boost::scoped_ptr<> as has been mentioned already.
In general though, if you need to make a class non-copyable, you should inherit from boost::noncopyable, i.e.
#include <boost/utility.hpp>
class myclass : boost::noncopyable
{
...
};
This does all the work of making it non-copyable and is nicely self-documenting.
You've got two choices:
boost::scoped_ptr already detailed by j_random_hacker, because it's non-copyable (doesn't share ownership like shared_ptr) and non-movable (doesn't transfer ownership like auto_ptr. auto_ptr has a copy constructor, but that one does not copy. It moves the original pointer to *this). boost::scoped_ptr is exactly what you need.
const auto_ptr doesn't allow transfer of ownership. And take your parameter by reference to const (auto_ptr<T> const&). If the writer of a function accepts by value instead, it still won't work if you try passing a const auto_ptr, because its copy constructor needs a non-const auto_ptr.
Until C++1x, boost::scoped_ptr is the best choice for your needs, or a const auto_ptr if you have to use official standard stuff (read this). In C++1x, you can use std::unique_ptr as a better alternative to auto_ptr, because you have to explicitly state when you want to transfer ownership. Trying to copy it will result in a compile time error. unique_ptr is detailed a little in this answer.
I've seen sometimes usage of simple DISABLE_COPY macros:
#define DISABLE_COPY(Class) \
Class(const Class &); \
Class &operator=(const Class &);
So it's a common practice to define copy constructor and assignment operator as private for your task.