You can fix that oversight, as illustrated in Chapter 14, “Reusing Code in C++,” but it would be nice if there were a neater solution. Let’s think about what is needed. When a function such as remodel() terminates, either normally or by throwing an exception, local variables are removed from the stack memory—so the memory occupied by the pointer ps is freed. It would be nice if, in addition, the memory pointed to by ps was freed. If ps had a destructor, that destructor could free the pointed-to memory when ps expires. Thus, the problem with ps is that it is just an ordinary pointer and not a class object having a destructor. If it were an object, you could have its destructor delete the pointed-to memory when the object expires. And that is the idea behind auto_ptr, unique_ptr, and shared_ptr. The auto_ptr template is the C++98 solution. C++11 deprecates auto_ptr and provides the other two as alternatives. However, although deprecated, auto_ptr has been used for years and may be your only choice if your compiler doesn’t support the other two.
Using Smart Pointers
These three smart pointer templates (auto_ptr, unique_ptr, and shared_ptr ) each defines a pointer-like object intended to be assigned an address obtained (directly or indirectly) by new. When the smart pointer expires, its destructor uses delete to free the memory. Thus, if you assign an address returned by new to one of these objects, you don’t have to remember to free the memory later; it will be freed automatically when the smart pointer object expires. Figure 16.2 illustrates the behavioral difference between auto_ptr and a regular pointer. The shared_ptr and unique_ptr share the same behavior in this situation.
Figure 16.2. A regular pointer versus auto_ptr.
To create one of these smart pointer objects, you include the memory header file, which includes the template definitions. Then you use the usual template syntax to instantiate the kind of pointer you require. The auto_ptr template, for instance, includes the following constructor:
template
public:
explicit auto_ptr(X* p =0) throw();
...};
(The throw() notation, recall, means this constructor doesn’t throw an exception. Like auto_ptr, it is deprecated.) Thus, asking for an auto_ptr object of type X gives you an auto_ptr object that points to type X:
auto_ptr
// (use in place of double * pd)
auto_ptr
// (use in place of string * ps)
Here new double is a pointer returned by new to a newly allocated chunk of memory. It is the argument to the auto_ptr
unique_ptr
shared_ptr
Thus, to convert the remodel() function, you would follow these three steps:
1. Include the memory header file.
2. Replace the pointer-to-string with a smart pointer object that points to string.
3. Remove the delete statement.
Here’s the function with those changes made using auto_ptr:
#include
void remodel(std::string & str)
{
std::auto_ptr
...
if (weird_thing())
throw exception();
str = *ps;
// delete ps; NO LONGER NEEDED
return;
}
Note that smart pointers belong to the std namespace. Listing 16.5 presents a simple program using all three of these smart pointers. (Your compiler will need to support the C++11 shared_ptr and unique_ptr classes.) Each use is placed inside a block so that the pointer expires when execution leaves the block. The Report class uses verbose methods to report when an object is created or destroyed.
Listing 16.5. smrtptrs.cpp
// smrtptrs.cpp -- using three kinds of smart pointers
// requires support of C++11 shared_ptr and unique_ptr
#include
#include
#include
class Report
{
private:
std::string str;
public:
Report(const std::string s) : str(s)
{ std::cout << "Object created!\n"; }
~Report() { std::cout << "Object deleted!\n"; }
void comment() const { std::cout << str << "\n"; }
};
int main()
{
{
std::auto_ptr
ps->comment(); // use -> to invoke a member function
}
{
std::shared_ptr
ps->comment();
}
{