I wrote a project using normal pointers and now I'm fed up with manual memory management.
What are the issues that one could anticipate during refactoring?
Until now, I already spent an hour replacing X* with shared_ptr<X> for types I want to automatically manage memory. Then I changed dynamic_cast to dynamic_pointer_cast. I still see many more errors (comparing with NULL, passing this to a function).
I know the question is a bit vague and subjective, but I think I can benefit from experience of someone who has already done this.
Are there some pitfalls?
Although it's easy to just use boost::shared_pointer everywhere, you should use the correct smart pointer as per ownership semantics.
In most cases, you will want to use std::unique_ptr by default, unless the ownership is shared among multiple object instances.
If you run into cyclical ownership problems, you can break up the cycles with boost::weak_ptr.
Also keep in mind that while passing shared_ptr's around, you should always pass them by const reference for performance reasons (avoids an atomic increment) unless you really want to confer ownership to a different entity.
Are there some pitfalls?
Yes, by murphy's law if you blindly replace every pointer with shared_ptr, it'll turn out that isn't what you wanted, and you'll spend next 6 months hunting bugs you introduced.
What are the issues that one could anticipate during refactoring?
Inefficient memory management, unused resources being stored longer than necessary, memory leaks (circular references), invalid reference counting (same pointer assigned to multiple different shared_pointers).
Do NOT blindly replace everything with shared_ptr. Carefully investigate program structure and make sure that shread_ptr is NEEDED and it represents EXACTLY what you want.
Also, make sure you use version control that supports easy branching (git or mercurial), so when you break something you can revert to previous state or run something similar to "git bisect" to locate problem.
obviously you need to replace X* with shared_ptr
Wrong. It depends on context. If you have a pointer that points into the middle of some array (say, pixel data manipulation), then you won't be able to replace it with shared_ptr (and you won't need to). You need to use shared_ptr only when you need to ensure automatic deallocation of object. Automatic deallocation of object isn't always what you want.
If you wish to stick with boost, you should consider if you want a boost::shared_ptr or a boost::scoped_ptr. A shared_ptr is a resource to be shared between classes, whereas a scoped_ptr sounds more like what you may want (at least in some places). A scoped_ptr will automatically delete the memory when it goes out of scope.
Be wary when passing a shared_ptr to a function. The general rule with shared_ptr is to pass by value so a copy is created. If you pass it by reference then the pointer's reference count will not be incremented. In this case, you might end up deleting a piece of memory that you wanted kept alive.
There is a case, however, when you might want to pass a shared_ptr by reference. That is, if you want the memory to be allocated inside a different function. In this case, just make sure that the caller still holds the pointer for the lifetime of the function it is calling.
void allocPtr( boost::shared_ptr< int >& ptrByRef )
{
ptrByRef.reset( new int );
*ptrByRef = 3;
}
int main()
{
boost::shared_ptr< int >& myPointer;
// I want a function to alloc the memory for this pointer.
allocPtr( myPointer ); // I must be careful that I still hold the pointer
// when the function terminates
std::cout << *ptrByRef << std::endl;
}
I'm listing the steps/issues involved. They worked for me, but I can't vouch that they are 100% correct
0) check if there could be cyclic shared pointers. If so, can this lead to memory leak? I my case, luckily, cycles need not be broken because if I had a cycle, the objects in the cycle are useful and should not be destroyed. use weak pointers to break cycles
1) you need to replace "most" X* with shared_ptr<X> . A shared_ptr is (only?) created immediately after every dynamic allocation of X . At all other times, it is copy constructed , or constructed with an empty pointer(to signal NULL) . To be safe (but a bit inefficient), pass these shared_ptrs only by reference . Anyways, it's likely that you never passed your pointers by reference to begin with => no additional change is required
2) you might have used dynamic_cast<X*>(y) at some places. replace that with
dynamic_pointer_cast<X>(y)
3) wherever you passed NULL(eg. to signal that a computation failed), pass an empty shared pointer.
4) remove all delete statements for the concerned types
5) make your base class B inherit from enable_shared_from_this<B>. Then wherever you passed this , pass, shared_from_this() . You might have to do static casting if the function expected a derived type . keep in mind that when you call shared_from_this(), some shared_ptr must already be owning this . In particular, don't call shared_from_this() in constructor of the class
I'm sure one could semi-automate this process to get a semantically equivalent but not necessarily very-efficient code. The programmer probably only needs to reason about cyclic reference(if any).
I used regexes a lot in many of these steps. It took about 3-4 hours. The code compiles and has executed correctly so far.
There is a tool that tries to automatically convert to smart pointers. I've never tried it. Here is a quote from the abstract of the following paper:
http://www.cs.rutgers.edu/~santosh.nagarakatte/papers/ironclad-oopsla2013.pdf
To enforce safety properties that are difficult to check statically,
Ironclad C++ applies dynamic checks via templated “smart
pointer” classes.
Using a semi-automatic refactoring tool, we have ported
nearly 50K lines of code to Ironclad C++
Related
I believe what I have just experienced is called "undefined behavior", but I'm not quite sure. Basically, I had an instance declared in an outer scope that holds addresses of a class. In the inner level I instantiated an object on the stack and stored the address of that instance into the holder.
After the inner scope had escaped, I checked to see if I could still access methods and properties of the removed instance. To my surprise it worked without any problem.
Is there a simple way to combat this? Is there a way I can clear deleted pointers from the list?
example:
std::vector<int*> holder;
{
int inside = 12;
holder.push_back(&inside);
}
cout << "deleted variable:" << holder[0] << endl;
Is there a simple way to combat this?
Sure, there are a number of ways to avoid this sort of problem.
The easiest way would be to not use pointers at all -- pass objects by value instead. i.e. In your example code, you could use a std::vector<int> instead of a std::vector<int *>.
If your objects are not copy-able for some reason, or are large enough that you think it will be too expensive to make copies of them, you could allocate them on the heap instead, and manage their lifetimes automatically using shared_ptr or unique_ptr or some other smart-pointer class. (Note that passing objects by value is more efficient than you might think, even for larger objects, since it avoids having to deal with the heap, which can be expensive... and modern CPUs are most efficient when dealing with contiguous memory. Finally, modern C++ has various optimizations that allow the compiler to avoid actually doing a data copy in many circumstances)
In general, retaining pointers to stack objects is a bad idea unless you are 100% sure that the pointer's lifetime will be a subset of the lifetime of the stack object it points to. (and even then it's probably a bad idea, because the next programmer who takes over the code after you've moved on to your next job might not see this subtle hazard and is therefore likely to inadvertently introduce dangling-pointer bugs when making changes to the code)
After the inner scope had escaped, I checked to see if I could still
access methods and properties of the removed instance. To my surprise
it worked without any problem.
That can happen if the memory where the object was hasn't been overwritten by anything else yet -- but definitely don't rely on that behavior (or any other particular behavior) if/when you dereference an invalid pointer, unless you like spending a lot of quality time with your debugger chasing down random crashes and/or other odd behavior :)
Is there a way I can clear deleted pointers from the list?
In principle, you could add code to the objects' destructors that would go through the list and look for pointers to themselves and remove them. In practice, I think that is a poor approach, since it uses up CPU cycles trying to recover from an error that a better design would not have allowed to be made in the first place.
Btw this is off topic but it might interest you that the Rust programming language is designed to detect and prevent this sort of error by catching it at compile-time. Maybe someday C++ will get something similar.
There is no such thing as deleted pointer. Pointer is just a number, representing some address in your process virtual address space. Even if stack frame is long gone, memory, that was holding it is still available, since it was allocated when thread started, so technically speaking, it is still a valid pointer, valid in terms, that you could dereference it and get something. But since object it was pointing is already gone, valid term will be dangling pointer. Moral is that if you have pointer to the object in the stack frame, there is no way to determine is it valid or not, not even using functions like IsBadReadPtr (Win32 API just for example). The best way to prevent such situations is avoid returning and storing pointers to the stack objects.
However, if you wish to track your heap allocated memory and automatically deallocate it after it is no longer used, you could utilize smart pointers (std::shared_ptr, boost::shared_ptr, etc).
I know unique_ptrs cannot be copied only moved and they have no reference counting. But we can have two smart pointers that share a resource:
Foo* f = new Foo;
auto p1 = std::unique_ptr<Foo>(f);
auto p2 = std::unique_ptr<Foo>(f);
Now both of these classes share a pointer to *f. Also, I know this will eventually cause UB because we will be doing double delete but still: What do we really mean by a unique_ptr being "unique" if this is possible?
Beside the fact that I do not believe that this is wanted or portable behaviour, I think that a unique_ptr is also a statement to other people working on the same project.
From the reference:
std::unique_ptr is a smart pointer that retains sole ownership of an
object through a pointer and destroys that object when the unique_ptr
goes out of scope. No two unique_ptr instances can manage the same
object.
As I understand this, the behaviour of the sample you showed is actually not wanted and should not be used at all.
For people without knowledge of the subject (aka programming for dummies): What the OP does is like having two girlfriends, not knowing of each other. You're fine until they find out. When they do, and they definitely will, you'll probably wish you wouldn't have played with the fire.
To understand the terminology, you have to contrast unique_ptr and shared_ptr:
the former should be the sole responsible for managing the resource it points to
the latter should be sharing this responsibility with a set of peers
Often times, you will hear the term ownership to describe the responsibility of cleaning up.
Now, like many things in C++, you can attempt to subvert the system: only the intention is described, it's up to you to uphold your end of the bargain.
Your question is akin to,
How is a crescent wrench a wrench when I can use it to drive nails in
to the wall?
In other words, just because you can incorrectly use a tool to do something that shouldn't be done with it, doesn't mean it can't do what it was designed to do.
A unique_ptr is unique in the sense that you won't make copies of the pointer if you use it correctly. It ensures that there's only one controlling object, and that the controlled object is destroyed properly when the container is destroyed.
This is about ownership semantics:
Sole or unique ownership (e.g. std::unique_ptr and the old friend std::auto_ptr): only one pointer at a time owns an object.
Shared ownership (e.g. std::shared_ptr, boost::intrusive_ptr, linked_ptr): many pointers share the same object.
It's unique because, when used correctly, it represents a unique ownership model - only one pointer gives access to, and controls the lifetime of, an object. Compare this to shared_ptr, which represents a shared ownership model - more than one pointer can be used to access and manage the same object.
As you point out, you can break that model by messing around with dumb pointers (either keeping hold of the one used to initialise the smart pointer, or by using get() or similar to bypass the ownership model). As always, it's up to the programmer to be careful not to do the wrong thing with dumb pointers. There is nothing a smart pointer can do to control the use of dumb pointers.
Not wishing to put words in the OP's mouth, but I think the issue they may be raising might be to do with naming. Perhaps they are saying something like:
'Aaaaah! It makes no sense! The language is a work of lunacy! Lunacy, I tell you! Run! Run! Save yourselves!'.
If that's what the OP is hinting at then...
Rather than look for 'meaning' in C++ words and symbols, just try to remember their actual effects. eg 'unique' doesn't mean 'One only', even though it appears to mean exactly that, it merely has the effect of indicating that the ptr should be used in certain ways and not others. Similarly, 'Private' does not mean private, but has an effect on how something is shared. 'Static' things can move, and 'move' keeps things where they are to avoid copying them to a new location.
All you have to do is read the documentation forever, and accept the pain.
See also 'Alice Through The Looking-Glass'.
As illustrated in the code here, the size of the object returned from make_shared is two pointers.
However, why doesn't make_shared work like the following (assume T is the type we're making a shared pointer to):
The result of make_shared is one pointer in size, which points to of allocated memory of size sizeof(int) + sizeof(T), where the int is a reference count, and this gets incremented and decremented on construction/destruction of the pointers.
unique_ptrs are only the size of one pointer, so I'm not sure why shared pointer needs two. As far as I can tell, all it needs a reference count, which with make_shared, can be placed with the object itself.
Also, is there any implementation that is implemented the way I suggest (without having to muck around with intrusive_ptrs for particular objects)? If not, what is the reason why the implementation I suggest is avoided?
In all implementations I'm aware of, shared_ptr stores the owned pointer and the reference count in the same memory block. This is contrary to what other answers are saying. Additionally a copy of the pointer will be stored in the shared_ptr object. N1431 describes the typical memory layout.
It is true that one can build a reference counted pointer with sizeof only one pointer. But std::shared_ptr contains features that absolutely demand a sizeof two pointers. One of those features is this constructor:
template<class Y> shared_ptr(const shared_ptr<Y>& r, T *p) noexcept;
Effects: Constructs a shared_ptr instance that stores p
and shares ownership with r.
Postconditions: get() == p && use_count() == r.use_count()
One pointer in the shared_ptr is going to point to the control block owned by r. This control block is going to contain the owned pointer, which does not have to be p, and typically isn't p. The other pointer in the shared_ptr, the one returned by get(), is going to be p.
This is referred to as aliasing support and was introduced in N2351. You may note that shared_ptr had a sizeof two pointers prior to the introduction of this feature. Prior to the introduction of this feature, one could possibly have implemented shared_ptr with a sizeof one pointer, but no one did because it was impractical. After N2351, it became impossible.
One of the reasons it was impractical prior to N2351 was because of support for:
shared_ptr<B> p(new A);
Here, p.get() returns a B*, and has generally forgotten all about the type A. The only requirement is that A* be convertible to B*. B may derive from A using multiple inheritance. And this implies that the value of the pointer itself may change when converting from A to B and vice-versa. In this example, shared_ptr<B> needs to remember two things:
How to return a B* when get() is called.
How to delete a A* when it is time to do so.
A very nice implementation technique to accomplish this is to store the B* in the shared_ptr object, and the A* within the control block with the reference count.
The reference count cannot be stored in a shared_ptr. shared_ptrs have to share the reference count among the various instances, therefore the shared_ptr must have a pointer to the reference count. Also, shared_ptr (the result of make_shared) does not have to store the reference count in the same allocation that the object was allocated in.
The point of make_shared is to prevent the allocation of two blocks of memory for shared_ptrs. Normally, if you just do shared_ptr<T>(new T()), you have to allocate memory for the reference count in addition to the allocated T. make_shared puts this all in one allocation block, using placement new and delete to create the T. So you only get one memory allocation and one deletion.
But shared_ptr must still have the possibility of storing the reference count in a different block of memory, since using make_shared is not required. Therefore it needs two pointers.
Really though, this shouldn't bother you. Two pointers isn't that much space, even in 64-bit land. You're still getting the important part of intrusive_ptr's functionality (namely, not allocating memory twice).
Your question seems to be "why should make_shared return a shared_ptr instead of some other type?" There are many reasons.
shared_ptr is intended to be a kind of default, catch-all smart pointer. You might use a unique_ptr or scoped_ptr for cases where you're doing something special. Or just for temporary memory allocations at function scope. But shared_ptr is intended to be the sort of thing you use for any serious reference counted work.
Because of that, shared_ptr would be part of an interface. You would have functions that take shared_ptr. You would have functions that return shared_ptr. And so on.
Enter make_shared. Under your idea, this function would return some new kind of object, a make_shared_ptr or whatever. It would have its own equivalent to weak_ptr, a make_weak_ptr. But despite the fact that these two sets of types would share the exact same interface, you could not use them together.
Functions that take a make_shared_ptr could not take a shared_ptr. You might make make_shared_ptr convertible to a shared_ptr, but you couldn't go the other way around. You wouldn't be able to take any shared_ptr and turn it into a make_shared_ptr, because shared_ptr needs to have two pointers. It can't do its job without two pointers.
So now you have two sets of pointers which are half-incompatible. You have one-way conversions; if you have a function that returns a shared_ptr, the user had better be using a shared_ptr instead of a make_shared_ptr.
Doing this for the sake of a pointer's worth of space is simply not worthwhile. Creating this incompatibility, creating two sets of pointers just for 4 bytes? That simply isn't worth the trouble that is caused.
Now, perhaps you would ask, "if you have make_shared_ptr why would you ever need shared_ptr at all?"
Because make_shared_ptr is insufficient. make_shared is not the only way to create a shared_ptr. Maybe I'm working with some C-code. Maybe I'm using SQLite3. sqlite3_open returns a sqlite3*, which is a database connection.
Right now, using the right destructor functor, I can store that sqlite3* in a shared_ptr. That object will be reference counted. I can use weak_ptr where necessary. I can play all the tricks I normally would with a regular C++ shared_ptr that I get from make_shared or whatever other interface. And it would work perfectly.
But if make_shared_ptr exists, then that doesn't work. Because I can't create one of them from that. The sqlite3* has already been allocated; I can't ram it through make_shared, because make_shared constructs an object. It doesn't work with already existing ones.
Oh sure, I could do some hack, where I bundle the sqlite3* in a C++ type who's destructor will destroy it, then use make_shared to create that type. But then using it becomes much more complicated: you have to go through another level of indirection. And you have to go through the trouble of making a type and so forth; the destructor method above at least can use a simple lambda function.
Proliferation of smart pointer types is something to be avoided. You need an immobile one, a movable one, and a copyable shared one. And one more to break circular references from the latter. If you start to have multiple ones of those types, then you either have very special needs or you are doing something wrong.
I have a honey::shared_ptr implementation that automatically optimizes to a size of 1 pointer when intrusive. It's conceptually simple -- types that inherit from SharedObj have an embedded control block, so in that case shared_ptr<DerivedSharedObj> is intrusive and can be optimized. It unifies boost::intrusive_ptr with non-intrusive pointers like std::shared_ptr and std::weak_ptr.
This optimization is only possible because I don't support aliasing (see Howard's answer). The result of make_shared can then have 1 pointer size if T is known to be intrusive at compile-time. But what if T is known to be non-intrusive at compile-time? In this case it's impractical to have 1 pointer size as shared_ptr must behave generically to support control blocks allocated both alongside and separately from their objects. With only 1 pointer the generic behavior would be to point to the control block, so to get at T* you'd have to first dereference the control block which is impractical.
Others have already said that shared_ptr needs two pointers because it has to point to the reference count memory block and the Pointed to Types memory Block.
I guess what you are asking is this:
When using make_shared both memory blocks are merged into one, and because the blocks sizes and alignment are known and fixed at compile time one pointer could be calculated from the other (because they have a fixed offset). So why doesn't the standard or boost create a second type like small_shared_ptr which does only contain one pointer.
Is that about right?
Well the answer is that if you think it through it quickly becomes a large hassle for very little gain. How do you make the pointers compatible? One direction, i.e. assigning a small_shared_ptr to a shared_ptr would be easy, the other way round extremely hard. Even if you solve this problem efficiently, the small efficiency you gain will probably be lost by the to-and-from conversions that will inevitably sprinkle up in any serious program. And the additional pointer type also makes the code that uses it harder to understand.
there are already a couple of questions regarding this topic, but I am still not sure what to do: Our codebase uses shared_ptr at many places. I have to admit that we did not define ownership clearly when writing it.
We have some methods like
void doSomething(shared_ptr<MyClass> ptr)
{
//doSomething() is a member function of a class, but usually won't store the ptr
ptr->foo();
...
}
After having discovered the first (indirect) circular dependencies I would like to correct the mistakes in our design. But I'm not exactly sure how. Is there any benefit in changing the method from above to
void doSomething(weak_ptr<MyClass> ptr)
{
shared_ptr<MyClass> ptrShared = ptr.lock();
ptrShared->foo();
...
}
?
I am also confused because some people say (including the Google Style guide) that in the first place it's important to get ownership correct (which would probably mean introduction of many weak_ptrs, e.g. in the example with the methods above, but also for many member variables that we have). Others say (see links below) that you should use weak_ptr to break cyclic dependencies. However, detecting them is not always easy, so I wonder if I really should use shared_ptr until I run into problems (and realize them), and then fix them??
Thanks for your thoughts!
See also
shared_ptr and weak_ptr differences
boost::shared_ptr cycle break with weak_ptr
boost, shared ptr Vs weak ptr? Which to use when?
We did not define ownership clearly.
You need to clearly define who owns what. There's no other way to solve this. Arbitrarily swapping out some uses of shared_ptr with weak_ptr won't make things better.
There is no benefit in changing your design above from shared_ptr to weak_ptr. Getting ownership right is not about using weak_ptrs, it's about managing who stores the shared_ptr for any significant length of time. If I pass a shared_ptr to a method, assuming I don't store that shared_ptr into a field in my object as part of that method, I haven't changed who owns that data.
In my experience the only reason for using weak_ptr is when you absolutely must have a cycle of pointers and you need to break that cycle. But first you should consider if you can modify your design to eliminate the cycle.
I usually discourage mixing shared_ptr's and raw pointers. It inevitably happens (though it probably shouldn't) that a raw pointer needs to be passed to a function that takes a shared_ptr of that type. A weak_ptr can be safely converted to a shared_ptr, with a raw pointer you're out of luck. Even worse, a developer inexperienced with shared_ptr's may create a new shared_ptr from that raw pointer and pass it to the function, causing that pointer to be deleted when the function returns. (I actually had to fix this bug in production code, so yes it does happen :) )
It sounds like you have a design problem. shared_ptr provides
a simple to use implementation for specific design solutions,
but it (nor anything else) can replace the design. Until you
have determined what the actual lifetime of each type of object
should be, you shouldn't be using shared_ptr. Once you've done
that, most of the shared_ptr/weak_ptr issues should disappear.
If, having done that, and determined that the lifetime of some
objects does depend on that of other objects, and there are
cycles in this dependency, you have to determine (at the design
level, again) how to manage those cycles---it's quite possible,
for example, that in those cases, shared_ptr isn't the correct
solution, or that many of the pointers involved are just for
navigation, and should be raw pointers.
At any rate, the answer to your question resides at the design
level. If you have to ask it when coding, then it's time to go
back to design.
Some people are right: you should at first have very clear picture about objects' ownership in your project.
shared_ptrs are shared, i.e. "owned by community". That might or might not be desirable. So I would advise to define ownership model and then to not abuse shared_ptr semantics and use plain pointers whenever ownership should not be "shared" more.
Using weak_ptrs would mask the problem further rather than fix it.
Right now, object ownership/deletion in my C++ project is manually tracked (via comments mostly). Almost every heap allocated object is created using a factory of sorts
e.g.
auto b = a->createInstanceOfB(); //a owns b
auto c = b->createInstanceOfC(); //b owns c
//auto k = new K(); //not in the code
...
//b is no longer used..
a->destroyInstanceOfB(b); //destroyInstanceOf calls delete on it
What benefits, if any, will smart pointers provide in this sitution?
It's not the creation you should worry about, it's the deletion.
With smart pointers (the reference counting kind), objects can be commonly owned be several other objects, and when the last reference goes out of scope, the object is deleted automatically. This way, you won't have to manually delete anything anymore, you can only leak memory when you have circular dependencies, and your objects are never deleted from elsewhere behind your back.
The single-owner-only type (std::auto_ptr) also relieves you of your deleting duty, but it only allows one owner at a time (though ownership can be transferred). This is useful for objects that you pass around as pointers, but you still want them automatically cleaned up when they go out of scope (so that they work well in containers, and the stack unrolling in the case of an exception works as expected).
In any case, smart pointers make ownership explicit in your code, not only to you and your teammates, but also to the compiler - doing it wrong is likely to produce either a compiler error, or a runtime error that is relatively easy to catch with defensive coding. In manually memory-managed code, it is easy to get the ownership situation wrong somewhere (due to misreading comments, or assuming things the wrong way), and the resulting bug is typically hard to track down - you'll leak memory, overwrite stuff that's not yours, the program crashes at random, etc.; these all have in common that the situation where the bug occurs is unrelated to the offending code section.
Smart pointers enforce ownership semantics- that is, it's guaranteed that the object will be freed correctly even in the case of exceptions. You should always use them purely because of the safety, even if they express only very simple semantics such as std::unique_ptr. Moreover, a pointer that enforces the semantics reduces the need to document it, and less documentation means less documentation to be out of date or incorrect- especially where multiple parts of the same program express the same semantics.
Ultimately, smart pointers reduce many sources of error and there's little reason not to use them.
If an object is only owned by one other object, and dies with it, fine. Still need to make sure there's no dangling references, but this is not the hard case.
The hard case, is where you share ownership. In that case, you will want to have smart-ptrs (or something) to automatically figure out when to actually delete an object.
Note that shared ownership is not necessary everywhere, and avoiding it will likely simplify things down the road when your product goes bloaty. :)