This page last updated on:
May 16, 2001
Contents and Related Links
CodeWrangler Pages
|
Overview
C++ provides a lot of facilities to write safer, more robust code, but
it doesn't protect from many kinds of memory problems. One of the most
common problems is to forget to define a copy constructor and destructor
when resources (typically heap memory) are allocated in the constructor.
There are four compiler-generated "give-me's", that can often lead to trouble:
-
Default constructor, if no constructor's are defined and a default constructor
is needed (i.e. an object is default constructed in application code).
Note that there may be parameters on a constructor, all of which have default
arguments, which results in a default constructor.
-
Copy constructor, if one is needed in the client / application code, which
can be in three situations:
-
Direct construction of an object by another of the same type
-
Pass an object into a function by value (creates a copy)
-
Return an object by value (creates a copy)
-
Assignment operator, when assigning an object to another of the same type.
-
Destructor, called whenever an object is destroyed.
An example class with the declarations of the above, to illustrate the
member function signatures of each:
class Employee {
public:
Employee (); // default ctor, see note above on default args, which also may be default ctor
~Employee(); // dtor
Employee(const Employee& ); // copy ctor
Employee& operator=(const Employee& ); // assign op, sometimes called copy assignment operator
};
There are three forms of direct copy construction syntax (in the client
/ application code), which result in equivalent results. The first form
is highly preferred (for efficiency, safe style, and consistency considerations):
Employee jack(jill); // Jill is an object of type Employee
Employee jack = jill; // easily confused with assignment
Employee jack = Employee(jill); // can be inefficient
An example function declaration, where a copy of the calling argument will
be made (copy constructor invoked):
void promoteEmployee (Employee);
An example function declaration of return by value, where an object copy
will be made on return:
Employee findOldestEmployee();
Safe C++ programming means that any kind of managed resources that have
been allocated in the constructor or elsewhere in the class implementation
code should have at least one constructor defined (whether it is a default
or non-default ctor), a copy constructor, an assignment operator, and a
destructor defined. There are many examples of resources that could be
allocated:
-
Heap memory (most typical)
-
Shared memory (through operating system specific calls)
-
Semaphores, mutexes, memory locks
-
File opens, closes
-
File or record lock, unlock
Good C++ style (for safe, consistent, readable code) recommends that every
class should either implement, disable (by making private), or comment
as 'implicit / compiler generated' the following member functions:
-
Default constructor
-
Copy constructor
-
Assignment operator
-
Destructor
If it is appropriate and correct logic for the compiler to generate these
member functions (implicitly), then it is usually considered safer and
more consistent for the compiler to do it (since people tend to not be
as complete or thorough as compilers).
The example Employee class declaration above would have all
of the critical member functions implemented. An example of recommended
style with the compiler generating all of these methods (this encourages
a future maintenance programmer to consider uncommenting if needed) :
class Employee {
public:
// Employee (); // default ctor, implicit
// ~Employee(); // dtor, implicit
// Employee(const Employee& ); // copy ctor, implicit
// Employee& operator=(const Employee& ); // assign op, implicit
};
An example where copying or assignment of an object will not be allowed
(appropriate for many types):
class Employee {
private:
Employee(const Employee& ); // copy ctor, private
Employee& operator=(const Employee& ); // assign op, private
};
|