Constructor Functions  «Prev  Next»
Lesson 2 Memory allocation for variables
Objective Review concepts of memory allocation/initialization of variables.

C++ Memory Allocation for Variables

An object's constructor is automatically called when the object is created. This means that it is called when the object's declaration is executed. If you are accustomed to thinking of a declaration statement as being passive, this is not the case for C++. In C++, a declaration statement is a statement that is executed. This distinction is not just academic. The code executed to construct an object may be quite significant. An object's constructor is called once for global or static local objects. For local objects, the constructor is called each time the object declaration is encountered.
  • C++ Allocators
    Each container has defined for it an allocator. Allocators manage memory allocation for a container. The default allocator is an object of class allocator, but you can define your own allocators, if needed, for specialized applications. For most uses, the default allocator is sufficient.

C and C++ provide memory allocation and initialization of variables through declarations that are definitions.
For example, in


void foo()
{
int n = 5;
double z[10] = { 0.0 };
struct gizmo { int i, j; } w = { 3, 4 };
. . .
}

all the variables are created at block entry when foo() is invoked. Upon exit from foo(), deallocation occurs automatically. Remember that an object is a class variable, which requires memory and some initial values. A class needs a mechanism to specify object creation and object destruction behavior so a client can use objects in a manner similar to native types. Classes use constructor and destructor member functions to manage class-defined objects.

Typical implementation of Memory Allocation for Variables

A typical implementation uses a runtime system stack. Thus, the int object n on a system with 4-byte integers get this allocated off the stack and initialized to the value 5. The gizmo object w requires 8 bytes to represent its two integer members. The array of double object z requires ten times sizeof(double) to store its elements. In each case, the system provides for the construction and initialization of these variables.
malloc will have to make a system call, to request memory from the OS. Since OS allocators usually work with larger fixed sized blocks of memory, malloc will not have make this call every time, only when it has exhausted the memory it currently has. For local variables, it really is up to the compiler. The standard makes no mention of a stack. However, most likely you are on a common architecture running a common OS and common compiler.
  • Dynamic Memory Allocation
    All of the code you have written up to now allocates space for data at compile time. You specify the variables and the arrays that you need in the code, and that is what will be allocated when the program starts, whether you need it or not. Working with a fixed set of variables in a program can be very restrictive, and it's often wasteful. Dynamic memory allocation is allocating the memory you need to store the data you are working with at runtime, rather than having the amount of memory predefined when the program is compiled. You can change the amount of memory your program has dedicated to it as execution progresses. By definition, dynamically allocated variables cannot be defined at compile time so they cannot be named in your source program. When you allocate memory dynamically, the space that is made available is identified by its address. The obvious and only place to store this address is in a pointer. With the power of pointers and the dynamic memory management tools in C++, writing this kind of flexibility into your programs is quick and easy. You can add memory to your application when it is needed, then release the memory you have acquired when you are done with it. Thus the amount of memory dedicated to an application can increase and decrease as execution progresses. Previously, I introduced the three kinds of storage duration that variables can have: 1) automatic, 2) static, and 3) dynamic and I discussed how variables of the first two varieties are created. Variables for which memory is allocated at runtime always have dynamic storage duration.



Dynamic Memory Allocation

A common programming practice is to
  1. allocate dynamic memory,
  2. assign the address of that memory to a pointer,
  3. use the pointer to manipulate the memory and
  4. deallocate the memory with delete when the memory is no longer needed.

If an exception occurs after successful memory allocation but before the delete statement executes, a memory leak could occur. The C++ standard provides class template auto_ptr in header file <memory> to deal with this situation.
An object of class auto_ptr maintains a pointer to dynamically allocated memory. When an auto_ptr object destructor is called (for example, when an auto_ptr object goes out of scope), it performs a delete operation on its pointer data member. Class template auto_ptr provides overloaded operators * and -> so that an auto_ptr object can be used just as a regular pointer variable is. Figure 4.2.3 demonstrates an auto_ptr object that points to a dynamically allocated object of class Integer (Figs.Figure 4.2.1-Figure 4.2.2).

class Integer
{
public:
    Integer( int i = 0 );     // Integer default constructor
    ~Integer();               // Integer destructor
    void setInteger( int i ); // function to set Integer
    int getInteger() const;   // function to return Integer
private:
    int value;
}; // end class Integer
Figure 4.2.1 Integer class definition

// Integer member function definitions.
#include <iostream>
#include "Integer.h"
using namespace std;

cout << "\nUsing the auto_ptr to manipulate the Integer\n";
ptrToInteger->setInteger( 99 ); // use auto_ptr to set Integer value

// use auto_ptr to get Integer value
cout << "Integer after setInteger: " << ( *ptrToInteger ).getInteger()
     << endl;
} // end main

Console Output:
Creating an auto_ptr object that points to an Integer
Constructor for Integer 7

Using the auto_ptr to manipulate the Integer
Integer after setInteger: 99

Destructor for Integer 99
Figure 4.2.2: Member function definitions of class Integer.

Line 15 of Fig. 4.2.3 creates auto_ptr object ptrToInteger and initializes it with a pointer to a dynamically allocated Integer object that contains the value 7. Line 18 uses the auto_ptr overloaded -> operator to invoke function setInteger on the Integer object that ptrToInteger manages. Line 21 uses the auto_ptr overloaded * operator to dereference ptrToInteger, then uses the dot (.) operator to invoke function getInteger on the Integer object. Like a regular pointer, an auto_ptr’s -> and * overloaded operators can be used to access the object to which the auto_ptr points.

// Demonstrating auto_ptr.
#include <iostream>
#include <memory>
using namespace std;

#include "Integer.h"

// use auto_ptr to manipulate Integer object
int main()
{
    cout << "Creating an auto_ptr object that points to an Integer\n";

    // "aim" auto_ptr at Integer object
    auto_ptr<Integer> ptrToInteger( new Integer( 7 ) );


cout << "\nUsing the auto_ptr to manipulate the Integer\n";
ptrToInteger->setInteger( 99 ); // use auto_ptr to set Integer value

// use auto_ptr to get Integer value
cout << "Integer after setInteger: " << ( *ptrToInteger ).getInteger()
     << endl;
} // end main
Figure 4.2.3: auto_ptr object manages dynamically allocated memory.

Expected Console Output:**
Creating an auto_ptr object that points to an Integer
Constructor for Integer 7

Using the auto_ptr to manipulate the Integer
Integer after setInteger: 99

Destructor for Integer 99

SEMrush Software