Dynamic allocation and deallocation

The next program [NEWDEL.CPP] contains the first example of the

new and delete operators. These perform dynamic allocation and

deallocation in much the same manner that malloc( ) and free( ) do in a

C implementation.

#include <iostream.h>

struct date {

int month;

int day;

int year;

};

void main()

{

int index, *point1, *point2;

point1 = &index;

*point1 = 77;

point2 = new int;

*point2 = 173;

cout << "The values are " << index << " " <<

*point1 << " " << *point2 << "\n";

point1 = new int;

point2 = point1;

*point1 = 999;

cout << "The values are " << index << " " <<

*point1 << " " << *point2 << "\n";

delete point1;

float *float_point1, *float_point2 = new float;

float_point1 = new float;

*float_point2 = 3.14159;

*float_point1 = 2.4 * (*float_point2);

delete float_point2;

delete float_point1;

date *date_point;

date_point = new date;

date_point->month = 10;

date_point->day = 18;

date_point->year = 1938;

cout << date_point->month << "/" << date_point->day << "/" <<

date_point->year << "\n";

delete date_point;

char *c_point;

c_point = new char[37];

delete c_point;

c_point = new char[sizeof(date) + 133];

delete c_point;

}

During the design of C++ it was felt that, since dynamic allocation and

deallocation are such a heavily used part of the C programming

language and would also be heavily used in C++, they should be a part

of the language, rather than a library add-on. The new and delete

operators are actually a part of the C++ language and are operators,

much like the addition operator or the assignment operator. They are

therefore very efficient and easy to use.

Lines 10 and 11 illustrate the use of pointers in the tradition of C and

line 12 illustrates the use of the new operator. This operator requires

one modifier which must be a type as illustrated here. The pointer

named point2 is now pointing at the dynamically allocated integer

variable which exists on the heap, and can be used in the same way that

any dynamically allocated variable is used in ANSI-C. Line 14 illustrates

displaying the value on the monitor which was assigned in line 13.

Line 16 allocates another new variable and line 17 causes point2 to

refer to the same dynamically allocated variable as point1 is pointing to.

In this case, the reference to the variable that point2 was previously

pointing to has been lost and it can never be used or deallocated. It is

lost on the heap until we return to the operating system when it will be

reclaimed for further use, so this is obviously not good practice. Note

that point1 is deallocated with the delete operator in line 21, and point2

can not actually be deleted. Since the pointer point1 itself is not

changed, it is actually still pointing to the original data on the heap. This

data could probably be referred to again using point1, but it would be

terrible programming practice since you have no guarantee what the

system will do with the pointer or the data. The data storage is returned

to the free list to be allocated in a subsequent call, and will soon be

reused in any practical program.

Since the delete operator is defined to do nothing if it is passed a NULL

value, it is legal to ask the system to delete the data pointed to by a

pointer with the value of NULL, but nothing will actually happen; this is

wasted code. The delete operator can only be used to delete data

allocated by a new operator. If the delete is used with any other kind of

data, the operation is undefined and anything can happen. According to

the ANSI standard, even a system crash is a legal result of this illegal

operation, and can be defined as such by the compiler writer.

In line 22 we declare some floating point variables; in C++ the variables

do not have to be declared at the beginning of a block. A declaration is

an executable statement and can therefore appear anywhere in a list of

executable statements. One of the float variables is allocated within the

declaration to illustrate that this can be done. Some of the same

operations are performed on these float type variables as were done on

the int types earlier. Some examples of the use of a structure are given

in the last four lines and should be self explanatory.

Finally, since the new operator requires a type to determine the size of

the dynamically allocated block, you may wonder how you can allocate

a block of arbitrary size. This is possible by using the construct

illustrated in line 37 where a block of 37 char sized entities, which will

be 37 bytes, is allocated. A block of 133 bytes greater than the size of

the date structure is allocated in line 39. It is therefore clear that the

new operator can be used with all of the flexibility of the malloc()

function which you are familiar with.

The standard functions which you have been using in C for dynamic

memory management, malloc( ), calloc( ), and free( ), are also available

for use in C++ and can be used in the same manner they were used in C.

The new and delete operators should not be intermixed with the older

function calls since the results may be unpredictable. If you are updating

code with the older function calls, continue to use them for any

additions to the code. If you are designing and coding a new program

you should use the newer constructs because they are a built in part of

the language rather than an add on and are therefore more efficient.

 

Pointers to functions

The next program [FUNCPNT.CPP] illustrates the use of a pointer to a

function. It must be pointed out that there is nothing new here: the

pointer to a function is available in ANSI-C as well as in C++ and

works in the manner described here for both languages. But, as it is not

regularly used by most C programmers, it is defined here as a sort of

refresher. If you are comfortable with the use of pointers to functions,

you can skip this discussion entirely.

#include <stdio.h>

void print_stuff(float data_to_ignore);

void print_message(float list_this_data);

void print_float(float data_to_print);

void (*function_pointer)(float);

void main()

{

float pi = 3.14159;

float two_pi = 2.0 * pi;

print_stuff(pi);

function_pointer = print_stuff;

function_pointer(pi);

function_pointer = print_message;

function_pointer(two_pi);

function_pointer(13.0);

function_pointer = print_float;

function_pointer(pi);

print_float(pi);

}

void print_stuff(float data_to_ignore)

{

printf("This is the print stuff function.\n");

}

void print_message(float list_this_data)

{

printf("The data to be listed is %f\n", list_this_data);

}

void print_float(float data_to_print)

{

printf("The data to be printed is %f\n", data_to_print);

}

There is nothing unusual about this program except for the pointer to a

function declared in line 5. This declares a pointer to a function which

returns nothing (void) and requires a single formal parameter, a float

type variable. You will notice that all three of the functions declared in

lines 2 to 4 fit this profile and are therefore candidates to be called with

this pointer.

Observe that in line 10 we call the function print_stuff( ) with the

parameter pi and in line 11 we assign the imaginatively-named function

pointer named function_pointer the value of print_stuff( ) and use the

function pointer to call the same function again in line 13. Lines 11 and

13 are therefore identical in what is accomplished because of the pointer

assignment in line 11. In lines 15 to 18, a few more illustrations of the

use of the function pointer are given. You will be left to study these on

your own.

Since we assigned the name of a function to a function pointer, and did

not get an assignment error, the name of a function must be a pointer to

that function. This is exactly the case. A function name is a pointer to

that function, but it is a pointer constant and cannot be changed. This is

exactly the case with arrays in ANSI-C; an array name is a pointer

constant to the first element of the array.

Since the name of the function is a constant pointer to that function, we

can assign the name of the function to a function pointer and use the

function pointer to call the function. The only caveat is that the return

value and the number and types of parameters must be identical. Most

C and C++ compilers will not (and in fact cannot) warn you of type

mismatches between the parameter lists when the assignments are

made. This is because the assignments are done at runtime when no

type information is available to the system, rather than at compile time

when all type information is available.

 

Prototypes and Compatible Types.

 

Links:  Home C Programming Guide  C++ programming Guide

Contact Me