Why three smart pointers? (Actually, there are four, but we won’t discuss weak_ptr
.) And why is auto_ptr
being deprecated?
Begin by considering assignment:
auto_ptr<string> ps (new string("I reigned lonely as a cloud."));
auto_ptr<string> vocation;
vocation = ps;
What should the assignment statement accomplish? If ps
and vocation
were ordinary pointers, the result would be two pointers pointing to the same string
object. That is not acceptable here because the program would wind up attempting to delete the same object twice—once when ps
expires, and once when vocation
expires. There are ways to avoid this problem:
• Define the assignment operator so that it makes a deep copy. This results in two pointers pointing to two distinct objects, one of which is a copy of the other.
• Institute the concept of ownership, with only one smart pointer allowed to own a particular object. Only if the smart pointer owns the object will its destructor delete the object. Then have assignment transfer ownership. This is the strategy used for auto_ptr
and for unique_ptr
, although unique_ptr
is somewhat more restrictive.
• Create an even smarter pointer that keeps track of how many smart pointers refer to a particular object. This is called reference counting. Assignment, for example, would increase the count by one, and the expiration of a pointer would decrease the count by one. Only when the final pointer expires would delete
be invoked. This is the shared_ptr
strategy.
The same strategies we’ve discussed for assignment, of course, would also apply to the copy constructors.
Each approach has its uses. Listing 16.6 shows an example for which auto_ptr
is poorly suited.
// fowl.cpp -- auto_ptr a poor choice
#include <iostream>
#include <string>
#include <memory>
int main()
{
using namespace std;
auto_ptr<string> films[5] =
{
auto_ptr<string> (new string("Fowl Balls")),
auto_ptr<string> (new string("Duck Walks")),
auto_ptr<string> (new string("Chicken Runs")),
auto_ptr<string> (new string("Turkey Errors")),
auto_ptr<string> (new string("Goose Eggs"))
};
auto_ptr<string> pwin;
pwin = films[2]; // films[2] loses ownership
cout << "The nominees for best avian baseball film are
";
for (int i = 0; i < 5; i++)
cout << *films[i] << endl;
cout << "The winner is " << *pwin << "!
";
cin.get();
return 0;
}
The nominees for best avian baseball film are
Fowl Balls
Duck Walks
Segmentation fault (core dumped)
The “core dumped” message should help fix in your memory that a misused auto_ptr
can be a problem. (The behavior for this sort of code is undefined, so you might encounter different behavior, depending on your system.) Here the problem is that the following statement transfers ownership from films[2]
to pwin
:
pwin = films[2]; // films[2] loses ownership
That causes films[2]
to no longer refer to the string. After an auto_ptr
gives up ownership of an object, it no longer provides access to the object. When the program goes to print the string pointed to by films[2]
, it finds the null pointer, which apparently is an unpleasant surprise.
Suppose you go back to Listing 16.6 but use shared_ptr
instead of auto_ptr
. (You’ll need a compiler that supports the C++11 shared_ptr
class.) Then the program runs fine and gives this output:
The nominees for best avian baseball film are
Fowl Balls
Duck Walks
Chicken Runs
Turkey Errors
Goose Eggs
The winner is Chicken Runs!
The difference occurs in this part of the program:
shared_ptr<string> pwin;
pwin = films[2];
This time both pwin
and films[2]
point to the same object, and the reference count is upped from 1 to 2. At the end of the program, pwin
, which was declared last, is the first object to have its destructor called. The destructor decreases the reference count to 1. Then the members of the array of shared_ptr
s are freed. The destructor for films[2]
decrements the count to 0 and frees the previously allocated space.
So with shared_ptr
, Listing 16.6 runs fine. With auto_ptr
it experienced a runtime crash. What happens if you use unique_ptr
? Like auto-ptr
, unique_ptr
incorporates the ownership model. Yet instead of crashing, the unique_ptr
version yields a compile-time error objecting to this line:
pwin = films[2];
Clearly, it is time to look further into the differences between these last two types.
18.189.171.125