Chapter 14

C++ and Memory Models

CHAPTER OUTLINE
14.1  INTRODUCTION

Memory is one of the critical resources of a computer system. It is limited and can also be exhausted. One should be conscious about the memory modules while an application is being developed. In this chapter, we will learn about stack, the heap, dynamic memory allocation, dynamic objects, and calling conventions.

14.2  MEMORY MODELS

The memory model sets the supportable size of code and data areas as shown in Figure 14.1. We need to specify an appropriate memory model before compiling and linking the source code. Selection of the memory model is done from the option menu of the C++ editor. The compiler option is selected, and one of the memory models is chosen. Using memory models, we can set the size limits of the data and code. C/C++ programs always use different segments for code and data. The memory model you opt for decides the default method of memory addressing, and the default memory model is small. Table 14.1 describes the properties of all memory models.

Fig. 14.1

Fig. 14.1 Memory organization

Tiny: Use the tiny model when memory is at an absolute premium. All four segment registers (CS, DS, ES, and SS) are initialized with the same address, and all addressing is accomplished using 16 bits. Total memory capacity in this case is 64 Kbytes. In short, memory capacity is abbreviated as KB. This means that the code, data, and stack should fit within the same 64-Kbytes segment. Programs are executed quickly if this model is selected. Near pointers are always used. Tiny model programs can be converted to .COM format.

Small: All codes should fit into a single 64-KB segment, and all data should fit into a second 64-KB segment. All pointers are 16 bits in length. Execution speed is similar to that in the tiny model. This model is used for average-sized programs. Near pointers are always used.

Medium: All data should fit into a single 64-KB segment. However, the code is allowed to use multiple segments. All pointers to data are 16 bits, but all jumps and calls require 32-bit addresses. With this, the access to data is fast. However, slower program execution is observed with this model. This model is suitable for big programs that do not store a large amount of data in memory. Far pointers are used for codes but not for data.

Compact: All codes should fit into a 64-KB segment, but the data can use multiple segments. However, no data item can surpass 64 KB. All pointers to data are 32 bits, but jumps and calls can use 16-bit addresses. Slow access to data and quick code execution will be observed in this setting.

 

Table 14.1 Memory models

Table 14.1

The compact model is preferred if your program is small, but you require to point a large amount of data. The compact model is the opposite of the medium model. Far pointers are used for data but not for code. The code is limited to 64 K, whereas the data have a 1-MB range. All functions are near by default, and all data pointers are far by default.

Large: Both code and data are allowed to use multiple segments. All pointers are 32 bits in length. However, no single data item can exceed 64 KB. Code execution is slower.

A large model is used for very big programs only. Far pointers are used for both code and data, both of which specify a 1-MB range. All functions and data pointers are far by default.

Huge: Both code and data are allowed to use multiple segments. Every pointer is 32 bits in length and it is different from large memory model only by pointer math and not by segments. Because of this huge model, code execution is slowest.

Use the Huge model for very big programs only. Far pointers are used for both code and data. Turbo C++ usually bounds the size of all data to 64K. The Huge memory model sets aside that limit, permitting data to hold more than 64K. This model permits multiple data segments (each 64K in size), up to 1 MB for code, and 64K for stack. All functions and data pointers are supposed to be far.

Segment and offset address

Every address has two parts: segment and offset. We can separate these address parts using the following two macros defined in dos.h header file.

FP_SEG() – This macro is used to obtain the segment address of the given pointer variable.

FP_OFF() – This macro is used to obtain the offset address of the given pointer variable.

The following programs illustrate the working of both these macros.

 

14.1 Write a program to obtain segment and offset address.

#include<dos.h>

#include<iostream.h>

#include<constream.h>

int main()

{

clrscr();

int ch;

cout<<“ Complete Address of ch:”<<&ch;

cout<<“ Segment address:”<<hex<<FP_SEG(&ch);

cout<<“ Offset address:”<<hex<<FP_OFF(&ch);

return 0;

}

OUTPUT

Complete Address of ch : 0x8f34fff2

Segment address : 8f34

Offset address : fff2

Fig. 14.2

Fig. 14.2 Segment and offset addresses

Explanation: In the above program, variable ch is declared. The first cout statement displays the whole address of the variable ch, that is, 0x8f34fff2. The macro FP_SEG() returns the segment address. The statement used is FP_SEG(&ch). Here, the address of ch is passed to the macro. In the same way, the offset address is obtained using the statement FP_OFF(&ch). You can observe the complete address as displayed above. The separated addresses are also similar. Figure 14.2 makes this point clearer.

far, huge, and near are keywords. They are summarized in Table 14.2.

 

Table 14.2 Keywords summary

Table 14.2

far pointer: A far pointer is a 32-bit pointer that contains both segment and offset address parts.

huge pointer: A huge pointer is 32 bits long, and it contains both segment and offset address parts.

near pointer: A near pointer is 16 bits long. It uses the contents of the CS or DS register for the segment part. The offset part of the address is stored in 16 bits near the pointer.

 

14.2 Write a program to declare far, near and huge pointer. Display their sizes.

#include<iostream.h>

#include<constream.h>

int main()

{

clrscr();

char far *f; // far pointer declaration

char near *n; // near pointer declaration

char huge *h; // huge pointer declaration

cout<<“ Size of far pointer:”<<sizeof(f);

cout<<“ Size of near pointer:”<<sizeof(n);

cout<<“ Size of huge pointer:”<<sizeof(h);

return 0;

}

OUTPUT

Size of far pointer: 4

Size of near pointer: 2

Size of huge pointer: 4

Explanation: In the above program, the pointers f, n, and h are declared. In the pointer declaration, the pointers are preceded by the keywords far, near, and huge. The sizeof() operators display the size of the pointers in bytes.

 

14.3 Write a program to use far pointer.

#include<iostream.h>

#include<constream.h>

int main()

{

clrscr();

char far *s; // pointer declaration

s=(char far*)0xB8000000L; // starting address

*s=‘W’; // displays character on the screen

return 0;

}

OUTPUT

W

Explanation: Character pointer *s, which is a far pointer, is declared. The address 0xB8000000L is assigned to it. It is a starting address outside the data segment. The address is converted into char far* type by applying type casting. The statement *s=‘W’ displays the character ‘W’ on the screen.

14.3  DYNAMIC MEMORY ALLOCATION

For the execution of the program, it is essential to bring the program into the main memory. When a program does not fit into the main memory, parts of it are brought into the main memory one by one, and program gets executed. Of course, the parts of the program that are not currently in the main memory are resident at the secondary memory, such as a floppy or a hard disk or any other magnetic disk. When a new segment of a program is to be moved into a full main memory, it should replace another segment that is already resident in the main memory. These are replacement policies, and their description is beyond the scope of this book. Hence, the programmer should not be concerned with the method of transfer of the program from secondary to primary memory or replacement policies.

The technique by which a program can obtain storage space in the main memory at run time is called dynamic allocation. In this method, the space for the program is allocated from the free space during the execution of the program. Memory requested by the program is allocated by the system from the memory heap. The free region of the memory is called the heap. The heap changes depending on the memory model used. Conceptually, the C++ programs are stored in the free space from the heap. Overall, 8086 memory models are described in this chapter. The amount of memory requirement is decided by how the program is designed. For example, if a program is developed with many recursive functions, then this is implemented with a stack.

The next section describes the C++ operators new and delete that can be used for achieving dynamic memory allocation and deallocation, respectively.

14.4  THE NEW AND delete OPERATORS

So far, we have used the new and delete operators in short programs, but for applying them in huge applications, we need to understand them completely. A small mistake in syntax may cause a critical error and possibly, a corrupt memory heap. Before beginning a study on the memory heap in detail, let us repeat a few points regarding new and delete operators.

  1. The new operator not only creates an object but also allocates memory.
  2. The new operator allocates memory from the heap that is also called a free store.
  3. The object created and the memory allocated by using the new operator should be deleted and memory should be released by the delete operator; otherwise, such a mismatch operation may corrupt the heap or may crash the system. According to the ANSI standard, it is a valid outcome for this invalid operation, and the compiler should have routines to handle such errors.
  4. The delete operator not only destroys objects but also releases allocated memory.
  5. The new operator creates an object, and it remains in the memory until it is released using the delete operator. Sometimes, the object deleted using the delete operator remains in the memory.
  6. If we send a null pointer to the delete operator, it is secure. Using delete to zero has no result.
  7. The statement delete x does not destroy the pointer x. It destroys the object associated with it.
  8. Do not apply C functions such as malloc(), realloc(), or free() with new and delete operators. These functions are unfit for object-oriented techniques (Table 14.3).
  9. Do not destroy the pointer repetitively or more than once. First time, the object is destroyed and memory is released. If second time the same object is deleted, the object is sent to the destructor, and, no doubt, it will corrupt the heap.
  10. If the object created is not deleted, it occupies the memory unnecessarily. It is a good habit to destroy the object and release the system resources.

Table 14.3 Difference between new and malloc()

new Operator
malloc()

Creates objects

Allocates memory

Returns pointer of relevant type

Returns void pointer

It is possible to overload a new operator

malloc() cannot be overloaded

14.4 Write a program to allocate memory to store 3 integers. Use new and delete operators for allocating and de-allocating memory. Initialize and display the values.

#include<iostream.h>

#include<conio.h>

int main()

{

clrscr();

int i, *p;

p=&i;

p=new int[3];

*p=2; // first element

*(p+1)=3; // second element

*(p+2)=4; // third element

cout<<“Value Address”;

for (int x=0;x<3;x++)

cout<<endl<<*(p+x)<<“ ”<<(unsigned)(p+x);

delete []p;

return 0;

}

OUTPUT

 

Value
Address
2
3350
3
3352
4
3354

Explanation: In the above program, integer variable i and pointer *p are declared. The pointer p is initialized with the address of variable i, and by using the new operator memory for three integers is allocated. The pointer *p can hold three integers in successive memory locations. The pointer variable is initialized with numerical values. The for loop is used to display the contents of the pointer *p. The delete operator releases the memory.

 

14.5 Write a program to allocate memory to store 3 floats. Use new and delete operators for allocating and de-allocating memory. Initialize and display the values.

#include<iostream.h>

#include<conio.h>

int main()

{

clrscr();

float i, *p;

p=&i;

p=new float[3];

for (int x=0;x<3;x++)

*(p+x)=x+3.11;

cout<<“Value Address”;

for ( x=0;x<3;x++)

cout<<endl<<*(p+x)<<“ ”<<(unsigned)(p+x);

delete []p;

return 0;

}

OUTPUT

 

Value
Address
3.11
4698
4.11
4702
5.11
4706

Explanation: In this program, the memory for three float numbers is allocated to the float pointer p. The first for loop initializes the pointer p, and the second for loop displays the values on the screen. The delete operator releases the memory.

 

14.6 Write a program to allocate memory for two objects. Initialize and display the contents and de-allocate the memory.

#include<iostream.h>

#include<conio.h>

struct boy

{

char *name;

int age;

};

int main()

{

clrscr();

boy *p;

p=new boy[2];

p->name=”Rahul”;

p->age=20;

(p+1)->name=”Raj”;

(p+1)->age=21;

for (int x=0;x<2;x++)

{

cout<<“ Name:”<<(p+x)->name<<endl<<“Age:”<<(p+x)->age;

}

delete []p;

return 0;

}

OUTPUT

Name : Rahul

Age : 20

Name : Raj

Age : 21

Explanation: In the above program, structure boy is declared, and memory for two objects is allocated to the pointer p. The pointer p is initialized, and the first for loop displays the contents of the pointer on the screen. Finally, the delete operator deallocates the memory.

 

14.7 Write a program to use new and delete operators to create and destroy objects.      code

#include<iostream.h>

#include<string.h>

#include<conio.h>

class student

{

private :

char name[25];

int age;

public :

student()

{

strcpy(name,“”);

age=0;

}

student (char *s,int g)

{

strcpy (name,s);

age=g;

}

void assign (char *s, int g)

{

strcpy(name,s);

age=g;

}

void display()

{

cout<<endl<<name<<“ ” <<age;

}

~student()

{ cout<<endl<<“In destructor”;}

};

void main(

{

clrscr();

student *s;

s=new student ;

s->assign (“santosh”,24);

student *t;

t=new student (“Amit”,15);

s->display();

t->display();

delete s;

delete t;

}

OUTPUT

santosh 24

Amit 15

In destructor

In destructor

Explanation: In the above program, the class student has two data members of character and integer type. The class also has zero-argument, two-argument constructors, and member function display(). The constructor initializes the object, and the display() function displays data on the screen.

In function main(), s and t are pointers to class student and using the new operator, memory is allocated and constructors are executed. The objects are initialized, and contents are displayed by calling display() function. The delete operator destroys the objects, and destructor() is executed after this.

14.5  HEAP CONSUMPTION

The heap is used to allocate memory during program execution, that is, run time. In assembly language, there is no such thing as a heap. All memory is for the programmer, and he/she can use it directly. In various ways, C/C++ is a better programming environment than assembly language. However, a cost has to be paid to use either C or C++. The cost is separation from the machine. We cannot use memory anywhere; we need to ask for it. The memory from which we receive information is called the heap.

Fig. 14.3

Fig. 14.3 Heap review

The C/C++ compiler may place automatic variables on the stack. It may store static variables earlier than loading the program. The heap is the piece of memory that we allocate. Figure 14.3 shows a view of the heap.

Local variables are stored in the stack, and the code is in code space. The local variables are destroyed when the function returns. Global variables are stored in the data area, and they are accessible by all functions. The heap can be considered a huge section of memory in which large numbers of memory locations are placed sequentially.

As stated earlier, all local variables are stored in the stack. As soon as the function execution is completed, local variables are destroyed, and the stack becomes empty. The heap is not cleared until the program execution is completed. It is the user’s task to free the memory. The memory allocated from the heap remains available until the user explicitly deallocates it.

While solving problems related to memory allocation, we always believe that large memory is available, and the heap is at no time short of memory. It is bad programming to depend on such guesses, which may cause an error in application. In C, the function malloc() is used to allocate the memory, and if this function fails to allocate the memory, it returns NULL pointer. By checking the return value of the function malloc(), failure or success of the memory allocation is tested, and appropriate sub-routines are executed.

C++ allows us to apply similar logic with new operators. C++ allows us two function pointers known as _new_handeleri and set_new_handler. The _new_handler holds a pointer to a function. It requires no arguments and returns void.

If a new operator fails to allocate the memory requested, it will invoke the function *_new_handler and again, it will attempt the memory allocation. By default, the function *_new_handler directly closes the program. It is also possible to substitute this handler with a function that releases memory. This can be directly accomplished by executing the function set_new_handler that returns a pointer to the handler.

 

14.8 Write a program to use set_new_handler function.

#include<iostream.h>

#include<new.h>

#include<stdlib.h>

#include<conio.h>

void m_warn()

{

cerr << “ Cannot allocate!”;

exit(1);

}

int main()

{

clrscr();

set_new_handler(m_warn);

char *k=new char[50];

cout<<“ First allocation:k=” << hex << long(k);

k=new char[64000U];

cout<<“Second allocation:k=” << hex << long(k);

set_new_handler(0); // Reset to default.

return 0;

}

OUTPUT

First allocation: k = 8fa40d48

Cannot allocate!

Explanation: In the above program, the set_new_handler is initialized with the function m_warn(). The m_wanr() displays a warning message. The variable p is a character pointer and using the new operator memory for 50 characters, it is allocated to the pointer p. The cout statement displays the starting address of the memory allocated.

Consider the statement p = new char [64000U]. In this statement, the new operator attempts to allocate memory to the pointer p. In case the new operator fails to allocate the memory, it calls the setnew_handler, and m_warn() function is executed.

14.6  OVERLOADING new AND delete OPERATORS

In C++, at any time when memory allocation and deallocation is concerned, the new and delete operators are used. These operators are invoked from the compiler’s library functions. They are a part of C++ language and are very effective. Similar to other operators, the new and delete operators are overloaded. The following programs illustrate this:

 

14.9 Write a program to overload new and delete operator.

#include<iostream.h>

#include<stdlib.h>

#include<new.h>

#include<conio.h>

void main()

{

clrscr();

void warning();

void *operator new (size_t, int);

void operator delete (void*);

char *t=new (‘#’) char [10];

cout<<endl<<“First allocation:p=”<<(unsigned)long(t)<<endl;

for (int k=0;k<10;k++)

cout<<t[k];

delete t;

t=new (‘*’) char [64000u];

delete t;

}

void warning()

{

cout<<“ insufficient memory”;

exit(1);

}

void *operator new (size_t os, int setv)

{

void *t;

t=malloc(os);

if (t==NULL) warning();

memset(t,setv,os);

return (t);

}

void operator delete(void *ss) {free(ss);}

OUTPUT

First allocation : p=3376

##########

Insufficient memory

Explanation: In the above program, the new and delete operators are overloaded. The size_t is used to determine the size of the object. The new operator calls the warning() function when the malloc() function returns NULL.

Consider the statement t = new (‘*’) char [64000u]. When memory allocation is requested by this statement, the new operator fails to allocate the memory and calls the function warning(). When called, the delete operator releases the memory using the free() function. Internally, in this program, malloc() and free() functions are used to allocate and deallocate the memory.

 

14.10 Write a program to overload new and delete operator.

#include<iostream.h>

#include<stdlib.h>

#include<new.h>

#include<conio.h>

void main()

{

clrscr();

void warning();

void *operator new (size_t);

void operator delete (void*);

char *t;

t=new char[5];

cout<<endl<<“First allocation:p=”<<(unsigned)long(t)<<endl;

for (int k=0;k<10;k++)

{

      *(t+k)=‘-’;

      cout<<*(t+k);

}

delete t;

t=new char [64000U];

delete t;

}

void warning()

{

cout<<“ Insufficient memory”;

exit(1);

}

void *operator new (size_t os)

{

void *t;

t=malloc(os);

if (t==NULL)

warning();

return (t);

}

void operator delete(void *ss) {free(ss);}

OUTPUT

First allocation: p=3374

----------

Insufficient memory

Explanation: The above program is similar to the previous one. In overloading of the new operator, only one argument is used. In the previous examples, the overloaded new and delete operators use the malloc() and free() function to allocate and deallocate memory. The same can be done with new and delete operators. The following programs illustrate the overloading of new and delete operators:

 

14.11 Write a program to overload new and delete operator. Read five numbers and find largest out of them.

#include<iostream.h>

#include<conio.h>

const int NUM=5;

class number

{

private :

int *series;

public :

void *operator new (size_t)

{

number *num;

num=::new number;

num->series=new int[NUM];

return num;

}

void operator delete ( void* n)

{

number *num;

num=(number *) n;

delete (int *) num->series;

::delete n;

}

void input();

int large (void);

};

void number ::input()

{

for (int i=0;i<NUM;i++)

{

      cout<<“Number[”<<i+1<<“]=”;

      cin>>series[i];

}

}

int number ::large()

{

int l=0;

for (int i=0;i<NUM;i++)

{

      if (l<series[i])

      l=series[i];

}

return l;

}

void main()

{

clrscr();

number *l_num=new number;

cout<<“Enter numbers”<<endl;

l_num->input();

cout<<“Largest Number:”<<l_num->large();

delete l_num;

}

OUTPUT

Enter numbers

Number[ 1] = 4

Number[ 2] = 7

Number[ 3] = 8

Number[ 4] = 2

Number[ 5] = 3

Largest Number : 8

Explanation: In the above program, the new and delete operators are overloaded. The new operator creates objects as well as allocates data members. The statement number *l_num = new number executes the overloaded new operator. The statement num =:: new number and ::delete n creates and destroys objects respectively. The number::new and number::delete are recursively executed. The statement num =::new number creates an object of the number class. If the scope access operator is omitted, the overloaded operator is executed recursively and results in a stack overflow.

If we prefix the scope access operator to the new operator, it will invoke the standard new operator built-in language instead of a defined one. The class number has a data member variable *series. The statement num->series = new int [NUM ] creates an array of NUM elements and allocates memory. It invokes the ::new operator.

Similarly, the overloaded delete operator releases the memory allocated by the new operator. It executes the overloaded operator function void operator delete (void* n).

14.7  OVERLOADING new AND delete IN CLASSES

The new and delete operators can be overloaded in a similar manner to other operators. It is interesting to observe their working when they are overloaded for multiple classes. The following program explains the working of overloaded new and delete operators in multiple classes:

 

14.12 Write a program to overload new and delete operators

#include<iostream.h>

#include<stdlib.h>

#include<string.h>

#include<conio.h>

#include<new.h>

#define T_BYTES 2

void warning()

{

cout<<endl<<“Cannot allocate memory”;

exit(1);

}

class boy

{

private :

char name [20];

int age;

float height;

public :

void *operator new (size_t);

void operator delete (void *t);

void assign(char *nm, int a, float h);

void display();

~boy();

};

struct limit

{

boy o;

int position ;

};

int signal=0;

struct limit *l=0;

void *boy ::operator new (size_t s)

{

int j;

if (signal==0)

{

      l=(limit*) malloc(s * T_BYTES);

      if (l==NULL) warning();

      for (j=0;j<T_BYTES;j++)

      l[j].position=0;

      signal=1;

      l[0].position=1;

return &l[0].o;

}

else

{

      for (j=0;j<T_BYTES;j++)

        {

          if (l[j].position==0)

      {

        l[j].position=1;

        return &l[j].o;

      }

      }

      warning();

}

      return NULL;

}

void boy ::operator delete ( void *t)

{

      if (t==NULL) return ;

      for (int j=0;j<T_BYTES;j++)

{

      if (t==&l[j].o)

      {

        l[j].position=0;

        strcpy(l[j].o.name,””);

        l[j].o.age=0;

        l[j].o.height=0.0;

      }

}

}

void boy::assign (char *nm, int a, float h)

{

strcpy (name,nm);

age=a;

height=h;

}

void boy::display()

{

cout<<endl<<name<<“ ”<<age<<“ ”<<height;

}

boy::~boy()

{

free(l);

cout<<endl<<“In Destructor (Memory released)”;

}

void main()

{

clrscr();

void warning();

set_new_handler (warning);

boy *b1,*b2,*b3;

b1=new boy;

b1->assign(“Sanjay”,20,4.5);

b2=new boy;

b2->assign(“Vijay”,22,5.1);

b1->display();

b2->display();

delete b1;

delete b2;

b1->display();

b2->display();

b1=new boy;

b2=new boy;

b3=new boy;

b3->display();

}

OUTPUT

Sanjay 20 4.5

Vijay 22 5.1

In Destructor (Memory released)

In Destructor (Memory released)

0    0

0    0

Cannot allocate memory

Explanation: In the above program, new and delete operators are overloaded class by class. The macro T_BYTES is initialized with the number of records for which the memory is to be occupied. The member function assign() is used to initialize the data members of the objects. The display() function is used to display the contents on the screen. The warning() function displays a warning message when called. It is associated with set_new_handler() and executed when memory allocation fails. The class boy is declared with three member variables.

In function main(), *b1, *b2, and *b3 are pointers to structure and by using the new operator, memory is allocated to them. Consider the statement l = (limit*) malloc (s* T_BYTES); here, the memory is allocated only for two records, and, hence, when we try to allocate memory for the third object, the function warning() is executed and it prompts the message “Cannot allocate memory.” The delete operator is used to release the memory allocated. The statements delete b1 and delete b2 release the memory allocated by these objects. After releasing the memory, if each function calls the display() function, the contents displayed of these objects is zero. This is obviously the effect of the delete operator.

14.8  EXECUTION SEQUENCE OF CONSTRUCTOR AND DESTRUCTOR

Overloaded new and delete operators functioning within the class are always static. We know that the static function can be invoked without specifying the object. Hence, this pointer is absent in the body of static functions. The compiler invokes the overloaded new operator and allocates memory before executing the constructor. The compiler also invokes the overloaded delete operator function and deallocates memory after execution of the destructor. The following program makes the concept clearer:

 

14.13 Write a program to display the sequence of execution of constructor and destructor in classes when operators new and delete are overloaded.

#include<iostream.h>

#include<stdlib.h>

#include<string.h>

#include<conio.h>

class boy

{

char name[10];

public :

void *operator new (size_t );

void operator delete (void *q);

boy();

~boy();

};

char limit[sizeof(boy)];

boy::boy()

{

      cout<<endl<<“In Constructor”;

}

boy::~boy()

{ cout<<endl<<“In Destructor”; }

void *boy ::operator new (size_t s )

{

cout<<endl<<“In boy ::new operator”;

return limit;

}

void boy::operator delete (void *q)

{ cout<<endl<<“In boy ::delete operator”; }

void main()

{

clrscr();

boy *e1;

e1=new boy;

delete e1;

}

OUTPUT

In boy ::new operator

In Constructor

In Destructor

In boy ::delete operator

Explanation: In this program, the new and delete operators are overloaded. The class also has a constructor and a destructor. The overloaded new operator function is executed first followed by the class constructor. We know that the constructor is always used to initialize members. The constructor also allocates memory by calling the new operator implicitly. Here, the new operator is overloaded. As soon as control of the program reaches the constructor, before executing any statement in the constructor it invokes the overloaded new operator.

Similarly, when an object goes out of scope, the destructor is executed. The destructor invokes the overloaded delete operator to release the memory allocated by the new operator.

14.9  SPECIFYING ADDRESS OF AN OBJECT

The compiler assigns an address to the object created. The programmer has no power over the address of the object. However, it is possible to create an object at the memory location given by the program explicitly. The address specified should be big enough to hold the object. The object should be created in such a way that it must be destroyed by invoking the destructor explicitly. The following program makes the concept clearer:

 

14.14 Write a program to create object at given memory address.

#include<iostream.h>

#include<constream.h>

class data

{

int j;

public:

data ( int k) {j=k;}

~data() { }

void *operator new ( size_t, void *u)

{

return (data *)u;

}

void show() {cout<<“j=”<<j;}

};

void main()

{

clrscr();

void *add;

add=(void*)0x420;

data *t=new (add) data(10);

cout<<“ Address of object:”<<t;

cout<<endl;

t->show();

t->data::~data();

}

OUTPUT

Address of object : 0x8f800420

j=10

Explanation: In the above program, the new operator is overloaded. The void pointer *add is declared and initialized with address 0x420. The pointer object *p is declared, and an address is assigned to it. The new operator is also invoked to allocate memory. In the same statement, the constructor is also invoked, and a value is passed to it. The member function show() displays the value of a member variable. Finally, the statement t->data::~data(); invokes the destructor to destroy the object. We can confirm the address of the object by displaying its address. The address of the object would be the same as the specified one.

14.10  DYNAMIC OBJECTS

As discussed earlier, C++ supports dynamic memory allocation/deallocation. C++ allocates memory and initializes the member variables. An object can be created at run time; such an object is called a dynamic object. The construction and destruction of the dynamic object is explicitly done by the programmer. The new and delete operators are used to allocate and deallocate memory to such objects. A dynamic object can be created using the new operator as follows:

ptr = new classname;

The new operator returns the address of the object created, and it is stored in the pointer ptr. The variable ptr is a pointer object of the same class. The member variable of the object can be accessed using the pointer and -> (arrow) operator. A dynamic object can be destroyed using the delete operator as follows:

delete ptr;

The delete operator destroys the object pointed by the pointer ptr. It also invokes the destructor of a class. The following program explains the creation and destruction of dynamic objects:

 

14.15 Write a program to create dynamic object.

#include<iostream.h>

#include<constream.h>

class data

{

int x,y;

public:

data()

{

cout<<“ Constructor”;

x=10;

y=50;

}

~data() {cout<<“ Destructor”;}

void display()

{

      cout<<“ x=”<<x;

      cout<<“ y=”<<y;

}

};

void main()

{

clrscr();

data *d; // declaration of object pointer

d=new data; // dynamic object

d->display();

delete d; // deleting dynamic object

}

OUTPUT

Constructor

x=10

y=50

Destructor

Explanation: The d is a pointer object. The statement d = new data; creates an anonymous object and assign its address to the pointer d. Such an object is called a dynamic object. The constructor is executed when the dynamic object is created. The statement d->display(); invokes the member function, and contents of the members are displayed. The statement delete d; destroys the object by releasing the memory and invoking the destructor.

14.11  CALLING CONVENTION

Calling convention means how parameters are pushed on the stack when a function is invoked. Table 14.4 describes the calling convention method of a few languages.

 

Table 14.4 Calling Conventions

Language
Order
Parameter Passed

Basic

In order (left to right)

By address

Fortran

In order (left to right)

By address

C

In reverse order (right to left)

By value and address

C++

In reverse order (right to left)

By value, address, and reference

Pascal

In order (left to right)

As values

In C/C++, parameters are passed from right to left; whereas in other languages such as basic, FORTRAN calling convention is in order from left to right. The following program explains the calling convention of C++:

 

14.16 Write a program to demonstrate calling convention of C++.

#include<iostream.h>

#include<constream.h>

void main()

{

clrscr();

void show (int,int);

int x=2,y=3;

show (x,y);

}

void show (int x, int y)

{

cout<<x;

cout<<endl;

cout<<y;

}

OUTPUT

2

3

Fig. 14.4

Fig. 14.4 Stack with arguments

Explanation: In this program, integer variables x and y are initialized to two and three, respectively. The show() function show() is invoked, and x and y are passed as per the statement show (x, y). The parameters are passed from right to left. The variable y is passed first followed by x. The values of variables with their position in the stack are displayed in Figure 14.4.

The value of the rightmost variable is pushed first followed by other variables in sequence.

SUMMARY
  1. The memory model sets the supportable size of code and data areas.
  2. The object created and the memory allocated by using the new operator should be deleted, and the memory should be released by the delete operator; otherwise, such a mismatch operation may corrupt the heap or may result in a system crash. According to the ANSI standard, it is a valid outcome for this invalid operation, and the compiler should have routines to handle such errors.
  3. The heap is used to allocate memory during program execution, that is, run time. The C/C++ compiler may place automatic variables on the stack. It may store static variables before loading the program. The heap is the piece of memory that we allocate.
  4. In C++, at any time when memory allocation and deallocation is concerned, the new and delete operators are used. These operators are a part of C++ language and are very effective. Similar to other operators, the new and delete operators are also overloaded.
  5. When a constructor and a destructor are executed, new and delete operators are invoked. When these operators are overloaded, the complier invokes these overloaded new and delete operators.
  6. An object can be created at run time; such an object is called a dynamic object. The construction and destruction of a dynamic object is explicitly done by the programmer.
  7. Calling convention means how parameters are pushed on the stack when a function is invoked. In C/C++, parameters are passed from right to left; whereas in basic, FORTRAN parameters are passed from left to right.
EXERCISES

(A) Answer the following questions

  1. What are the different types of memory models?
  2. Explain the properties of new and delete operators.
  3. What do you mean by a heap? Explain it in detail.
  4. Explain overloading of new and delete operators.
  5. What do you mean by a dynamic object? How are such objects created?
  6. What do you mean by calling conventions?
  7. Explain the process of calling convention in C/C++.
  8. Explain the use of set_new_handler.
  9. Explain the differences between the operator new and malloc() function.
  10. What are segment and offset addresses?
  11. Explain the use of macros FP_SEG() and FP_OFF().

(B) Answer the following by selecting the appropriate option

  1. The dynamic objects are created
    1. at run time
    2. compile time
    3. both (a) and (b)
    4. none of the above
  2. The new operator is used to
    1. allocate memory
    2. deallocate memory
    3. delete objects
    4. none of the above
  3. The delete operator is used to
    1. allocate memory
    2. deallocate memory
    3. create objects
    4. none of the above
  4. In C/c++, when a function is called, parameters are passed
    1. from right to left
    2. from left to right
    3. from top to bottom
    4. none of the above
  5. In c++, it is possible to pass values to functions by
    1. call by value
    2. call by address
    3. call by reference
    4. all of the above

(C) Attempt the following programs

  1. Write a program to create a dynamic object.
  2. Write a program to allocate memory for 10 integers to a pointer. Assign and display 10 integers. Destroy the object as well.
  3. Write a program to invoke a function with parameters. How are parameters passed? Are they passed from right to left or from left to right? Explain.
  4. Write a program to create an object at a specified memory location.

 

 

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset