LinuxDevCenter.com
oreilly.comSafari Books Online.Conferences.

advertisement


C++ Memory Management: From Fear to Triumph, Part 3

by George Belotsky
08/07/2003

Techniques for C++ Memory Management.

The first article in this series covered common C++ memory errors, while the second described the general nature of the C++ memory management mechanism. Now it's time to present a list of simple, powerful techniques that you can use to deal with memory in your C++ programs. It is best not to read this article in isolation; the previous articles will help you use the techniques presented here much more effectively.

An Ownership-Consistent SimpleString -- the Classic Example Continued

The first article of this series demonstrated a classic dangling reference by defining a class called SimpleString. Subsequent discussion in the second article showed that the problem in SimpleString is actually caused by divergent assumptions about memory ownership among the methods of that class. (The concept of memory ownership is critical: you should read about it now if you have not done so already.) In the original SimpleString, the destructor is written as if SimpleString objects own the memory used for their character buffers. The default copy constructor and assignment operator, however, act as though something else were responsible for that memory.

One way to make all of SimpleString behave consistently is to write our own copy constructor and assignment operator for the class, instead of relying on the default versions of these methods supplied by the C++ compiler. The following two examples illustrate this approach to fixing SimpleString.

Secure Programming Cookbook for C and C++

Related Reading

Secure Programming Cookbook for C and C++
Recipes for Cryptography, Authentication, Input Validation & More
By John Viega, Matt Messier

Example 1. simplestring.h

//*** SIMPLESTRING DECLARATION *** 

#ifndef EXAMPLE_SIMPLE_STRING
#define EXAMPLE_SIMPLE_STRING

class SimpleString {
  
public:

  explicit SimpleString(char* data = "");  //Use 'explicit' keyword to disable
                                           //automatic type conversions --
                                           //generally a good idea.

  //Copy constructor and assignment operator.
  SimpleString(const SimpleString& original);  
  SimpleString& operator=(const SimpleString& right_hand_side);

  virtual ~SimpleString();   //Virtual destructor, in case someone inherits
                             //from this class.

  virtual const char* to_cstr() const;  //Get a read-only C string.

  //Many other methods are needed to create a complete string class.
  //This example implements only a tiny subset of these, in order
  //to keep the discussion focused.

  //N.B. no 'inline' methods -- add inlining later, if needed for
  //optimization.

private:
  char* data_p_;   //Distinguish private class members: a trailing underscore
                   //in the name is one common method.
  
};

#endif

//*** END: SIMPLESTRING DECLARATION ***

Example 2. simplestring.cpp

//*** SIMPLESTRING IMPLEMENTATION *** 

#include <cstring>

#include "simplestring.h"

using namespace std;

//Constructor 
SimpleString::SimpleString(char* data_p) :
  data_p_(new char[strlen(data_p)+1]) {
  strcpy(data_p_,data_p);
}

//Copy constructor.
SimpleString::SimpleString(const SimpleString& original) :
  data_p_(new char[strlen(original.data_p_)+1]) {
  strcpy(data_p_,original.data_p_);
}

//Assignment operator.
SimpleString& 
SimpleString::operator=(const SimpleString& right_hand_side) {
  
  //It is possible for the caller to request assignment to self 
  //(i.e. "a = a;").  Do nothing in this case, or a serious
  //error will result.
  if (this != &right_hand_side) {

    //Allocate a new buffer first.  If this fails (i.e. throws
    //an exception), everything is still consistent. 
    char* data_p = new char[strlen(right_hand_side.data_p_)+1];

    //Now, delete the old buffer, and start using the new one.
    delete [] data_p_;  //(1)
    data_p_ = data_p;   //(2)
    
    //Copy the data over from the right hand side.  We checked
    //before that this is not self assignment, so we are safe.
    //Otherwise, we would have already destroyed this data
    //in statements (1) and (2)!
    strcpy(data_p_,right_hand_side.data_p_);
  }

  //This allows assignments to be chained (i.e. "a = b = c = d;").
  return *this;
}

//Destructor
SimpleString::~SimpleString() {
  //N.B. Use of 'delete []' corresponds to previous use of 'new []'.
  //     Using just 'delete' here would be a disaster.
  delete [] data_p_;
}

//Returns a read-only C string representation.
const char* SimpleString::to_cstr() const {
  return data_p_;
}

//*** END: SIMPLESTRING IMPLEMENTATION ***

As you can see, providing your own copy constructor and assignment operator is quite a bit of work. Fortunately, there are alternatives that will often make the defaults work as intended. The C++ standard library offers solutions for many such situations, for example. Smart pointers are another possibility and so is the Training Wheels class. Still, there will always be cases where a good design requires custom constructors and assignment operators.

Pages: 1, 2, 3, 4

Next Pagearrow




Linux Online Certification

Linux/Unix System Administration Certificate Series
Linux/Unix System Administration Certificate Series — This course series targets both beginning and intermediate Linux/Unix users who want to acquire advanced system administration skills, and to back those skills up with a Certificate from the University of Illinois Office of Continuing Education.

Enroll today!


Linux Resources
  • Linux Online
  • The Linux FAQ
  • linux.java.net
  • Linux Kernel Archives
  • Kernel Traffic
  • DistroWatch.com


  • Sponsored by: