Is it a good practice to always use smart pointers?
I find smart pointers to be a lot more comfortable than raw pointers. So is it a good idea to always use smart pointers? ( Please note that I am from Java background and hence don't much like the idea of explicit memory management. So unless there are some serious performance issues with smart pointers, I'd like to stick with them. )
Note: Though I come from Java background, I understand the implementation of smart pointers and the concepts of RAII quite well. So you can take this knowledge for granted from my side whe开发者_C百科n posting an answer. I use static allocation almost everywhere and use pointers only when necessary. My question is merely: Can I always use smart pointers in place of raw pointers???
Given the several edits, I have the impression that a comprehensive summary would be useful.
1. When not to
There are two situations where you should not use smart pointers.
The first is the exact same situation in which you should not use a C++
class in fact. IE: DLL boundary if you do not offer the source code to the client. Let say anecdotal.
The second happens much more often: smart manager means ownership. You may use pointers to point at existing resources without managing their lifetime, for example:
void notowner(const std::string& name)
{
Class* pointer(0);
if (name == "cat")
pointer = getCat();
else if (name == "dog")
pointer = getDog();
if (pointer) doSomething(*pointer);
}
This example is constrained. But a pointer is semantically different from a reference in that it may point to an invalid location (the null pointer). In this case, it's perfectly fine not to use a smart pointer in its stead, because you don't want to manage the lifetime of the object.
2. Smart managers
Unless you are writing a smart manager class, if you use the keyword delete
you are doing something wrong.
It is a controversial point of view, but after having reviewed so many example of flawed code, I don't take chances any longer. So, if you write new
you need a smart manager for the newly allocated memory. And you need it right now.
It does not mean you are less of a programmer! On the contrary, reusing code that has been proved to work instead of reinventing the wheel over and over is a key skill.
Now, the real difficulty start: which smart manager ?
3. Smart pointers
There are various smart pointers out of there, with various characteristics.
Skipping std::auto_ptr
which you should generally avoid (its copy semantic is screwed).
scoped_ptr
: no overhead, cannot be copied or moved.unique_ptr
: no overhead, cannot be copied, can be moved.shared_ptr
/weak_ptr
: some overhead (reference counting), can be copied.
Usually, try to use either scoped_ptr
or unique_ptr
. If you need several owners try to change the design. If you can't change the design and really need several owners, use a shared_ptr
, but beware of references cycles that ought to be broken using a weak_ptr
somewhere in the midst.
4. Smart containers
Many smart pointers are not meant to be copied, therefore their use with the STL containers are somewhat compromised.
Instead of resorting to shared_ptr
and its overhead, use smart containers from the Boost Pointer Container. They emulate the interface of classic STL containers but store pointers they own.
5. Rolling your own
There are situations when you may wish to roll your own smart manager. Do check that you did not just missed some feature in the libraries your are using beforehand.
Writing a smart manager in the presence of exceptions is quite difficult. You usually cannot assume that memory is available (new
may fail) or that Copy Constructor
s have the no throw
guarantee.
It may be acceptable, somewhat, to ignore the std::bad_alloc
exception and impose that Copy Constructor
s of a number of helpers do not fail... after all, that's what boost::shared_ptr
does for its deleter D
template parameter.
But I would not recommend it, especially for a beginner. It's a tricky issue, and you're not likely to notice the bugs right now.
6. Examples
// For the sake of short code, avoid in real code ;)
using namespace boost;
// Example classes
// Yes, clone returns a raw pointer...
// it puts the burden on the caller as for how to wrap it
// It is to obey the `Cloneable` concept as described in
// the Boost Pointer Container library linked above
struct Cloneable
{
virtual ~Cloneable() {}
virtual Cloneable* clone() const = 0;
};
struct Derived: Cloneable
{
virtual Derived* clone() const { new Derived(*this); }
};
void scoped()
{
scoped_ptr<Cloneable> c(new Derived);
} // memory freed here
// illustration of the moved semantics
unique_ptr<Cloneable> unique()
{
return unique_ptr<Cloneable>(new Derived);
}
void shared()
{
shared_ptr<Cloneable> n1(new Derived);
weak_ptr<Cloneable> w = n1;
{
shared_ptr<Cloneable> n2 = n1; // copy
n1.reset();
assert(n1.get() == 0);
assert(n2.get() != 0);
assert(!w.expired() && w.get() != 0);
} // n2 goes out of scope, the memory is released
assert(w.expired()); // no object any longer
}
void container()
{
ptr_vector<Cloneable> vec;
vec.push_back(new Derived);
vec.push_back(new Derived);
vec.push_back(
vec.front().clone() // Interesting semantic, it is dereferenced!
);
} // when vec goes out of scope, it clears up everything ;)
Smart pointers do perform explicit memory management, and if you don't understand how they are doing it, you are in for a world of trouble when programming with C++. And remember that memory isn't the only resource that they manage.
But to answer your question you should prefer smart-pointers as a first approximation to a solution, but possibly be prepared to ditch them when necessary. You should never use pointers (or any sort) or dynamic allocation when it can be avoided. For example:
string * s1 = new string( "foo" ); // bad
string s2( "bar" ); // good
Edit: To answer your suplementary question "Can I always use smart pointers in place of raw pointers??? Then, no you can't. If (for example) you need to implement your own version of operator new, you would have to make it return a pointer, not a smart pointer.
Usually you shouldn't use pointers (smart or otherwise) if you don't need them. Better make local variables, class members, vector elements and similar items normal objects instead of pointers to objects. (Since you come from Java you're probably tempted allocate everything with new
, which is not recommended.)
This approach ("RAII") saves you from worrying about pointers most of the time.
When you have to use pointers it depends on the situation and why exactly you need pointers, but usually smart pointers can be used. It might not be always (in bold) be the best option, but this depends on the specific situation.
A good time not to use smart pointers, is at the interface boundary of a DLL. You don't know whether other executables will be built with the same compiler/libraries. Your system's DLL calling convention won't specify what standard or TR1 classes look like, smart pointers included.
Within an executable or library, if you want to represent ownership of the pointee, then smart pointers are on average the best way to do it. So it's fine to want to always use them in preference to raw. Whether you actually can always use them is another matter.
For a concrete example when not to - suppose you are writing a representation of a generic graph, with vertices represented by objects and edges represented by pointers between the objects. The usual smart pointers will not help you: graphs can be cyclic, and no particular node can be held responsible for the memory management of other nodes, so shared and weak pointers are insufficient. You might for example put everything in a vector and use indices instead of pointers, or put everything in a deque and use raw pointers. You could use shared_ptr
if you wanted, but it won't add anything except overhead. Or you could look for mark-sweep GC.
A more marginal case: I prefer to see functions take a parameter by pointer or reference, and promise not to retain a pointer or reference to it, rather than take a shared_ptr
and leave you wondering whether maybe they retain a reference after they return, maybe if you modify the referand ever again you'll break something, etc. Not retaining references is something that often isn't documented explicitly, it just goes without saying. Maybe it shouldn't, but it does. Smart pointers imply something about ownership, and falsely implying that can be confusing. So if your function takes a shared_ptr
, be sure to document whether it can retain a reference or not.
In many situations, I believe they are definitely the way to go (less messy cleanup code, reduced risk of leaks, etc.). However there is some very slight extra expense. If I were writing some code that had to be as fast as possible (say a tight loop that had to do some allocation and a free), I would probably not use a smart pointer in hopes of eking out a bit more speed. But I doubt that it would make any measurable difference in most situations.
In general, no you cannot use smart pointers always. For example, when you use other frameworks that don't use smart pointer (like Qt), you have to use raw pointers too.
If you are handling a resource, you should always use RAII techniques, with in the case of memory means using some form or another of a smart pointer (note: smart, not shared_ptr
, choose the smart pointer that is most appropriate for your specific use case). It is the only way to avoid leaks in the presence of exceptions.
There are still cases where raw pointers are necessary, when resource management is not handled through the pointer. In particular they are the only way of having a resettable reference. Think of keeping a reference into an object whose lifetime cannot be explicitly handled (member attribute, object in the stack). But that is a very specific case that I have only seen once in real code. In most cases, using a shared_ptr
is a better approach to sharing an object.
My take on smart pointers: GREAT when it's hard to know when deallocation could happen (say inside an try/catch block, or inside a function that calls a function (or even a constructor!) that could throw you out of your current function), or adding better memory management to a function that has returns everywhere in the code. Or putting pointers in containers.
Smart pointers, however, have a cost that you might not want to pay all over your program. If memory management is easy to do by hand ("Hmm, I know that when this function ends I need to delete these three pointers, and I know that this function will run to completion"), then why waste the cycles having the computer do it?
Yes BUT i have gone several projects without the use of a smart pointer or any pointers. Its good practice to use containers such as deque, list, map etc. Alternatively i use references when possible. Instead of passing in a pointer i pass a reference or const reference and its almost always illogical to delete/free a reference so i never have issue there (typically i create them on the stack by writing { Class class; func(class, ref2, ref3); }
It is. Smart pointer is one of the cornerstones of the old Cocoa (Touch) ecosystem. I believe it keeps impacting the new.
精彩评论