- Next chapter
- Previous chapter
- Table of contents

# Chapter 5: Classes and memory allocation

> We're always interested in getting feedback. E-mail us if you like this guide, if you think that important material is omitted, if you encounter errors in the code examples or in the documentation, if you find any typos, or generally just if you feel like e-mailing. Mail to Frank Brokken or use an e-mail form. Please state the concerned document version, found in the title.

In contrast to the set of functions which handle memory allocation in C (i.e., `malloc()` etc.), the operators `new` and `delete` are specifically meant to be used with the features that C++ offers. Important differences between `malloc()` and `new` are:

- The function `malloc()` doesn't `know' what the allocated memory will be used for. E.g., when memory for `int`s is allocated, the programmer must supply the correct expression using a multiplication by `sizeof(int)`. In contrast, `new` requires the use of a type; the `sizeof` expression is implicitly handled by the compiler.
- The only way to initialize memory which is allocated by `malloc()` is to use `calloc()`, which allocates memory and resets it to a given value. In contrast, `new` can call the constructor of an allocated object where initial actions are defined. This constructor may be supplied with arguments.
- All C-allocation functions must be inspected for `NULL`-returns. In contrast, the `new`-operator provides a facility called a new_handler (cf. section 4.3.3) which can be used instead of the explicit checks for `NULL`-returns.

The relationship between `free()` and `delete` is analogous: `delete` makes sure that when an object is deallocated, a corresponding destructor is called.

The automatic calling of constructors and destructors when objects are created and destroyed, has a number of consequences which we shall discuss in this chapter. Many problems encountered during C program development are caused by incorrect memory allocation or memory leaks: memory is not allocated, not freed, not initialized, boundaries are overwritten, etc.. C++ does not `magically' solve these problems, but it does provide a number of handy tools.

Unfortunately, the very frequently used `str...()` functions, like `strdup()` are all `malloc()` based, and should therefore preferably not be used anymore in C++ programs. Instead, a new set of corresponding functions, based on the operator `new`, are preferred.

For the function `strdup()` a comparable function `char *strdupnew(char const *str)` could be developed as follows:

        char *strdupnew(char const *str)
        {
            return (strcpy(new char [strlen(str) + 1], str));
        }

Similar functions could be developed for comparable `malloc()`-based `str...()` and other functions.

In this chapter we discuss the following topics:

- the assignment operator (and operator overloading in general),

- the `this` pointer,

- the copy constructor.

## 5.1: Classes with pointer data members

In this section we shall again use the class `Person` as example:

        class Person
        {
            public:
                // constructors and destructor
                Person();
                Person(char const *n, char const *a,
                       char const *p);
                ~Person();

                // interface functions
                void setname(char const *n);
                void setaddress(char const *a);
                void setphone(char const *p);

                char const *getname(void) const;
                char const *getaddress(void) const;
                char const *getphone(void) const;

            private:
                // data fields
                char *name;
                char *address;
                char *phone;
        };

In this class the destructor is necessary to prevent that memory, once allocated for the fields `name`, `address` and `phone`, becomes unreachable when an object ceases to exist. In the following example a `Person` object is created, after which the data fields are printed. After this the `main()` function stops, which leads to the deallocation of memory. The destructor of the class is also shown for illustration purposes.

Note that in this example an object of the class `Person` is also created and destroyed using a pointer variable; using the operators `new` and `delete`.

        Person::~Person()
        {
            delete name;
            delete address;
            delete phone;
        }

        int main()
        {
            Person
                kk("Karel", "Rietveldlaan",
                    "050 542 6044"),
                *bill = new Person("Bill Clinton",
                       "White House",
                       "09-1-202-142-3045");

            printf("%s, %s, %s\n"
                   "%s, %s, %s\n",
                kk.getname(), kk.getaddress(), kk.getphone(),
                bill->getname(), bill->getaddress(), bill->getphone());

            delete bill;

            return (0);
        }

The memory occupied by the object `kk` is released automatically when `main()` terminates: the C++ compiler makes sure that the destructor is called. Note, however, that the object pointed to by `bill` is handled differently. The variable `bill` is a pointer; and a pointer variable is, even in C++, in itself no `Person`. Therefore, before `main()` terminates, the memory occupied by the object pointed to by `bill` must be explicitly released; hence the statement `delete` `bill`. The operator `delete` will make sure that the destructor is called, thereby releasing the three strings of the object.

## 5.2: The assignment operator

Variables which are `struct`s or `classes` can be directly assigned in C++ in the same way that `struct`s can be assigned in C. The default action of such an assignment is a straight bytewise copy from one compound variable to another.

Let us now consider the consequences of this default action in a program statement such as the following:

        void printperson(Person const &p)
        {
            Person
                tmp;

            tmp = p;
            printf("Name:     %s\n"
                    "Address:  %s\n"
                    "Phone:    %s\n",
                tmp.getname(), tmp.getaddress(), tmp.getphone());
        }

We shall follow the execution of this function step by step.

- The function `printperson()` expects a reference to a `Person` as its parameter `p`. So far, nothing extraordinary is happening.

- The function defines a local object `tmp`. This means that the default constructor of `Person` is called, which -if defined properly- resets the pointer fields `name`, `address` and `phone` of the `tmp` object to zero.

- Next, the object referenced by `p` is copied to `tmp`. By default this means that `sizeof(Person)` bytes from `p` are copied to `tmp`.

  Now a potentially dangerous situation has arisen. Note that the actual values in `p` are pointers, pointing to allocated memory. Following the assignment this memory is addressed by two objects: `p` and `tmp`.

- The potentially dangerous situation develops into an acutely dangerous situation when the function `printperson()` terminates: the object `tmp` is destroyed. The destructor of the class `Person` releases the memory pointed to by the fields `name`, `address` and `phone`: unfortunately, this memory is also in use by `p`....

  The incorrect assignment is illustrated in figure 3.

  figure 3: Private data and public interface functions of the class Person, using bytewise assignment

Having executed `printperson()`, the object which was referenced by `p` now contain pointers to deallocated memory.

This action is undoubtedly not a desired effect of a function like the above. The deallocated memory will likely become occupied during subsequent allocations: the pointer members of `p` have effectively become wild pointers, as they don't point to allocated memory anymore.

In general it can be concluded that every class containing pointer data members is a potential candidate for trouble. It is of course possible to prevent such troubles, as will be discussed in the next section.

### 5.2.1: Overloading the assignment operator

Obviously, the right way to assign one `Person` object to another, is not to copy the contents of the object bytewise. A better way is to make an equivalent object; one with its own allocated memory, but which contains the same strings.

The `right' way to duplicate a `Person` object is illustrated in figure 4.

figure 4: Private data and public interface functions of the class Person, using the `correct' assignment.

There is a number of solutions for the above wish. One solution consists of the definition of a special function to handle assignments of objects of the class `Person`. The purpose of this function would be to create a copy of an object, but one with its own `name`, `address` and `phone` strings. Such a member function might be:

        void Person::assign(Person const &other)
        {
            // delete our own previously used memory
            delete name;
            delete address;
            delete phone;

            // now copy the other Person's data
            name = strdupnew(other.name);
            address = strdupnew(other.address);
            phone = strdupnew(other.phone);
        }

Using this tool we could rewrite the offending function `printperson()`:

        void printperson(Person const &p)
        {
            Person
                tmp;

            // make tmp a copy of p, but with its own allocated
            // strings
            tmp.assign(p);

            printf("Name:     %s\n"
                    "Address:  %s\n"
                    "Phone:    %s\n",
                tmp.getname(), tmp.getaddress(), tmp.getphone());

            // now it doesn't matter that tmp gets destroyed..
        }

In itself this solution is valid, although it is a purely symptomatic solution. This solution requires that the programmer uses a specific member function instead of the operator `=`. The problem, however, remains if this rule is not strictly adhered to. Experience learns that errare humanum est: a solution which doesn't enforce exceptions is therefore preferable.

The problem of the assignment operator is solved by means of operator overloading: the syntactic possibility C++ offers to redefine the actions of an operator in a given context. Operator overloading was mentioned earlier, when the operators `<<` and `>>` were redefined for the usage with streams as `cin`, `cout` and `cerr` (see section 3.1.2).

Overloading the assignment operator is probably the most common form of operator overloading. However, a word of warning is appropriate: the fact that C++ allows operator overloading does not mean that this feature should be used at all times. A few rules are:

- Operator overloading should be used in situations where an operator has a defined action, but when this action is not desired as it has negative side effects. A typical example is the above assignment operator in the context of the class `Person`.

- Operator overloading can be used in situations where the usage of the operator is common and when no ambiguity in the meaning of the operator is introduced by redefining it. An example may be the redefinition of the operator `+` for a class which represents a complex number. The meaning of a `+` between two complex numbers is quite clear and unambiguous.

- In all other cases it is preferable to define a member function, instead of redefining an operator.

Using these rules, operator overloading is minimized which helps keep source files readable. An operator simply does what it is designed to do. Therefore, in our vision, the operators insertion (`<<`) and extraction (`>>`) operators in the context of streams are unfortunate: the stream operations do not have anything in common with the bitwise shift operations.

5.2.1.1: The function 'operator=()'

To achieve operator overloading in the context of a class, the class is simply expanded with a `public` function stating the particular operator. A corresponding function, the implementation of the overloaded operator, is thereupon defined.

For example, to overload the addition operator `+`, a function `operator+()` must be defined. The function name consists of two parts: the keyword `operator`, followed by the operator itself.

In our case we define a new function `operator=()` to redefine the actions of the assignment operator. A possible extension to the class `Person` could therefore be:

        // new declaration of the class
        class Person
        {
            public:
                ...
                void operator=(Person const &other);
                ...
            private:
                ...
        };

        // definition of the function operator=()
        void Person::operator=(Person const &other)
        {
            // deallocate old data
            delete name;
            delete address;
            delete phone;

            // make duplicates of other's data
            name = strdupnew(other.name);
            address = strdupnew(other.address);
            phone = strdupnew(other.phone);
        }

The function `operator=()` presented here is the first version of the overloaded assignment operator. We shall present better and less bug-prone versions shortly.

The actions of this member function are similar to those of the previously proposed function `assign()`, but now its name makes sure that this function is also activated when the assignment operator `=` is used. There are actually two ways to call this function, as illustrated below:

        Person
            pers("Frank", "Oostumerweg 17", "403 2223"),
            copy;

        // first possibility
        copy = pers;

        // second possibility
        copy.operator=(pers);

It is obvious that the second possibility, in which `operator=()` is explicitly stated, is not used often. However, the code fragment does illustrate the two ways of calling the same function.

## 5.3: The this pointer

As we have seen, a member function of a given class is always called in the context of some object of the class. There is always an implicit `substrate' for the function to act on. C++ defines a keyword, `this`, to address this substrate (Note that ``this`' is not available in the not yet discussed `static` member functions.)

The `this` keyword is a pointer variable, which always contains the address of the object in question. The `this` pointer is implicitly declared in each member function (whether `public` or `private`). Therefore, it is as if in each member function of the class `Person` would contain the following declaration:

        extern Person *this;

A member function like `setname()`, which sets a `name` field of a `Person` to a given string, could therefore be implemented in two ways: with or without the `this` pointer:

        // alternative 1: implicit usage of this
        void Person::setname(char const *n)
        {
            delete name;
            name = strdupnew(n);
        }

        // alternative 2: explicit usage of this
        void Person::setname(char const *n)
        {
            delete this->name;
            this->name = strdupnew(n);
        }

Explicit usage of the `this` pointer is not used very frequently. However, there exist a number of situations where the `this` pointer is really needed.

### 5.3.1: Preventing self-destruction with this

As we have seen, the operator `=` can be redefined for the class `Person` in such a way that two objects of the class can be assigned, leading to two copies of the same object.

As long as the two variables are different ones, the previously presented version of the function `operator=()` will behave properly: the memory of the assigned object is released, after which it is allocated again to hold new strings. However, when an object is assigned to itself (which is called auto-assignment), a problem occurs: the allocated strings of the receiving object are first released, but this also leads to the release of the strings of the right-hand side variable, which we call self-destruction. An example of this situation is illustrated below:

        void fubar(Person const &p)
        {
            p = p;          // auto-assignment!
        }

In this example it is perfectly clear that something unnecessary, possibly even wrong, is happening. But auto-assignment can also occur in more hidden forms:

        Person
            one,
            two,
            *pp;

        pp = &one;
        ...
        *pp = two;
        ...
        one = *pp;

The problem of the auto-assignment can be solved using the `this` pointer. In the overloaded assignment operator function we simply test whether the address of the right-hand side object is the same as the address of the current object: if so, no action needs to be taken. The definition of the function `operator=()` then becomes:

        void Person::operator=(Person const &other)
        {
            // only take action if address of current object
            // (this) is NOT equal to address of other
            // object(&other):

            if (this != &other)
            {
                delete name;
                delete address;
                delete phone;

                name = strdupnew(other.name);
                address = strdupnew(other.address);
                phone = strdupnew(other.phone);
            }
        }

This is the second version of the overloaded assignment function. One, yet better version remains to be discussed.

As a subtlety, note the usage of the address operator '`&`' in the statement

        if (this != &other)

The variable `this` is a pointer to the `current' object, while `other` is a reference; which is an `alias' to an actual `Person` object. The address of the other object is therefore `&other`, while the address of the current object is `this`.

### 5.3.2: Associativity of operators and this

According to C++'s syntax, the associativity of the assignment operator is to the right-hand side. I.e., in statements like:

        a = b = c;

the expression `b = c` is evaluated first, and the result is assigned to `a`.

The implementation of the overloaded assignment operator so far does not permit such constructions, as an assignment using the member function returns nothing (`void`). We can therefore conclude that the previous implementation does circumvent an allocation problem, but is syntactically not quite right.

The syntactical problem can be illustrated as follows. When we rewrite the expression `a = b = c` to the form which explicitly mentions the overloaded assignment member functions, we get:

        a.operator=(b.operator=(c));

This variant is syntactically wrong, since the sub-expression `b.operator=(c)` yields `void`; and the class `Person` contains no member functions with the prototype `operator=(void)`.

This problem can also be remedied using the `this` pointer. The overloaded assignment function expects as its argument a reference to a `Person` object. It can also return a reference to such an object. This reference can then be used as an argument for a nested assignment.

It is customary to let the overloaded assignment return a reference to the current object (i.e., `*this`), as a `const` reference: the receiver is not supposed to alter the `*this` object.

The (final) version of the overloaded assignment operator for the class `Person` thus becomes:

        // declaration in the class
        class Person
        {
            public:
                ...
                Person const &operator=(Person const &other)
                ...
        };

        // definition of the function
        Person const &Person::operator=(Person const &other)
        {
            // only take action when no auto-assignment occurs
            if (this != &other)
            {
                // deallocate own data
                delete address;
                delete name;
                delete phone;

                // duplicate other's data
                address = strdupnew(other.address);
                name = strdupnew(other.name);
                phone = strdupnew(other.phone);
            }

            // return current object, compiler will make sure
            // that a const reference is returned
            return (*this);
        }

## 5.4: The copy constructor: Initialization vs. Assignment

In the following sections we shall take a closer look at another usage of the operator `=`. For this, we shall use a class `String`. This class is meant to handle allocated strings, and its interface is as follows:

        class String
        {
            public:
                // constructors, destructor
                String();
                String(char const *s);
                ~String();

                // overloaded assignment
                String const &operator=(String const &other);

                // interface functions
                void set(char const *data);
                char const *get(void);

            private:
                // one data field: ptr to allocated string
                char *str;
        };

Concerning this interface we remark the following:

- The class contains a pointer `char *str`, possibly pointing to allocated memory. Consequently, the class needs a constructor and a destructor.

  A typical action of the constructor would be to set the `str` pointer to 0. A typical action of the destructor would be to release the allocated memory.

- For the same reason the class has an overloaded assignment operator. The code of this function would look like:

              String const &String::operator=(String const &other)
              {
                  if (this != &other)
                  {
                      delete str;
                      str = strdupnew(other.str);
                  }
                  return (*this);
              }

- The class has, besides a default constructor, a constructor which expects one string argument. Typically this argument would be used to set the string to a given value, as in:

              String
                  a("Hello World!\n");

- The only interface functions are to set the string part of the object and to retrieve it.

Now let's consider the following code fragment. The statement references are discussed following the example:

        String
            a("Hello World\n"),             // see (1)
            b,                              // see (2)
            c = a;                          // see (3)

        int main()
        {
            b = c;                          // see (4)
            return (0);
        }

- Statement 1: this statement shows an initialization. The object `a` is initialized with a string ``Hello World''. This construction of the object `a` therefore uses the constructor which expects one string argument.

  It should be noted here that this form is identical to

              String
                  a = "Hello World\n";

  Even though this piece of code uses the operator `=`, this is no assignment: rather, it is an initialization, and hence, it's done at construction time by a constructor of the class `String`.

- Statement 2: here a second `String` object is created. Again a constructor is called. As no special arguments are present, the default constructor is used.

- Statement 3: again a new object `c` is created. A constructor is therefore called once more. The new object is also initialized. This time with a copy of the data of object `a`.

  This form of initializations has not yet been discussed. As we can rewrite this statement in the form

              String
                  c(a);

  it suggests that a constructor is called, with as argument a (reference to a) `String` object. Such constructors are quite common in C++ and are called copy constructors. More properties of these constructors are discussed below.

- Statement 4: here one object is assigned to another. No object is created in this statement. Hence, this is just an assignment, using the overloaded assignment operator.

The simple rule emanating from these examples is that whenever an object is created, a constructor is needed. All constructors have the following characteristics:

- Constructors have no return values.

- Constructors are defined in functions having the same names as the class to which they belong.

- The argument list of constructors can be deduced from the code. The argument is either present between parentheses or following a `=`.

Therefore, we conclude that, given the above statement (3), the class `String` must be rewritten to define a copy constructor:

        // class definition
        class String
        {
            public:
                ...
                String(String const &other);
                ...
        };

        // constructor definition
        String::String(String const &other)
        {
            str = strdupnew(other.str);
        }

The actions of copy constructors are comparable to those of the overloaded assignment operators: an object is duplicated, so that it contains its own allocated data. The copy constructor function, however, is simpler in the following respect:

- A copy constructor doesn't need to deallocate previously allocated memory: since the object in question has just been created, it cannot already have its own allocated data.

- A copy constructor never needs to check whether auto-duplication occurs. No variable can be initialized with itself.

Besides the above mentioned quite obvious usage of the copy constructor, the copy constructor has other important tasks. All of these tasks are related to the fact that the copy constructor is always called when an object is created and initialized with another object of its class. The copy constructor is called even when this new object is a hidden or temporary variable.

- When a function takes an object as argument, instead of, e.g., a pointer or a reference, C++ calls the copy constructor to pass a copy of an object as the argument. This argument, which usually is passed via the stack, is therefore a new object. It is created and initialized with the data of the passed argument.

  This is illustrated in the following code fragment:

              void func(String s)         // no pointer, no reference
              {                           // but the String itself
                  puts(s.get());
              }

              int main()
              {
                  String
                      hi("hello world");

                  func(hi);
                  return (0);
              }

  In this code fragment `hi` itself is not passed as an argument, but instead a temporary(stack) variable is created using the copy constructor. This temporary variable is known within `func()` as `s`. Note that if `func()` would have been defined using a reference argument, extra stack usage and a call to the copy constructor would have been avoided.

- The copy constructor is also implicitly called when a function returns an object.

  This situation occurs when, e.g., a function returns keyboard input in a `String` format:

              String getline()
              {
                  char
                      buf [100];          // buffer for kbd input

                  gets(buf);              // read buffer

                  String
                      ret = buf;          // convert to String

                  return(ret);            // and return it
              }

  A hidden `String` object is here initialized with the return value `ret` (using the copy constructor) and is returned by the function. The local variable `ret` itself ceases to exist when `getline()` terminates.

To demonstrate that copy constructors are not called in all situations, consider the following. We could rewrite the above function `getline()` to the following form:

        String getline()
        {
            char
                buf [100];          // buffer for kbd input

            gets(buf);              // read buffer
            return (buf);           // and return it
        }

This code fragment is quite valid, even though the return value `char *` doesn't match the prototype `String`. In this situation, C++ will try to convert the `char *` to a `String`. It can do so given a constructor expecting a `char *` argument. This means that the copy constructor is not used in this version of `getline()`. Instead, the constructor expecting a `char *` argument is used.

Contrary to the situation we encountered with the default constructor, the default copy constructor remains available once a constructor (any constructor) is defined explicitly. The copy constructor can be redefined, but it will not disappear once another constructor is defined.

### 5.4.1: Similarities between the copy constructor and operator=()

The similarities between on one hand the copy constructor and on the other hand the overloaded assignment operator are reinvestigated in this section. We present here two primitive functions which often occur in our code, and which we think are quite useful. Note the following features of copy constructors, overloaded assignment operators, and destructors:

- The duplication of (private) data occurs (1) in the copy constructor and (2) in the overloaded assignment function.

- The deallocation of used memory occurs (1) in the overloaded assignment function and (2) in the destructor.

The two above actions (duplication and deallocation) can be coded in two private functions, say `copy()` and `destroy()`, which are used in the overloaded assignment operator, the copy constructor, and the destructor. When we apply this method to the class `Person`, we can rewrite the code as follows.

First, the class definition is expanded with two `private` functions `copy()` and `destroy()`. The purpose of these functions is to copy the data of another object or to deallocate the memory of the current object unconditionally. Hence these functions implement `primitive' functionality:

        // class definition, only relevant functions are shown here
        class Person
        {
            public:
                // constructors, destructor
                Person(Person const &other);
                ~Person();

                // overloaded assignment
                Person const &operator=(Person const &other);
            private:
                // data fields
                char
                    *name,
                    *address,
                    *phone;

                // the two primitives
                void copy(Person const &other);
                void destroy(void);
        };

Next, we present the implementations of the functions `copy()` and `destroy()`:

        // copy(): unconditionally copy other object's data
        void Person::copy(Person const &other)
        {
            name = strdupnew(other.name);
            address = strdupnew(other.address);
            phone = strdupnew(other.phone);
        }

        // destroy(): unconditionally deallocate data
        void Person::destroy ()
        {
            delete name;
            delete address;
            delete phone;
        }

Finally the three `public` functions in which other object's memory is copied or in which memory is deallocated are rewritten:

        // copy constructor
        Person::Person (Person const &other)
        {
            // unconditionally copy other's data
            copy(other);
        }

        // destructor
        Person::~Person()
        {
            // unconditionally deallocate
            destroy();
        }

        // overloaded assignment
        Person const &Person::operator=(Person const &other)
        {
            // only take action if no auto-assignment
            if (this != &other)
            {
                destroy();
                copy(other);
            }
            // return (reference to) current object for
            // chain-assignments
            return (*this);
        }

What we like about this approach is that the destructor, copy constructor and overloaded assignment functions are completely standard: they are independent of a particular class, and their implementations can therefore be used in every class. Any class dependencies are reduced to the implementations of the private member functions `copy()` and `destroy()`.

## 5.5: Conclusion

Two important extensions to classes have been discussed in this chapter: the overloaded assignment operator and the copy constructor. As we have seen, classes with pointer data which address allocated memory are potential sources of semantic errors. The two introduced extensions represent the standard ways to prevent unintentional loss of allocated data.

The conclusion is therefore: as soon as a class is defined in which pointer data-members are used, a destructor, an overloaded assignment function and a copy constructor should be implemented.

- Next chapter
- Previous chapter
- Table of contents