Contents

Dynamic Memory

new and delete Operators in C++ For Dynamic Memory

Dynamic memory allocation in C/C++ allows programmers to manually manage memory usage, specifically allocating memory on the heap. Unlike stack-allocated memory for local and static variables, dynamic allocation provides the flexibility to allocate and deallocate memory at runtime, which is especially useful for structures like linked lists and trees.

Applications of Dynamic Memory Allocation

One key application of dynamic memory allocation is the ability to handle variable-sized memory requirements, which isn’t feasible with static allocations (except for variable-length arrays). This flexibility is crucial in numerous scenarios, such as creating dynamic data structures.

Differences from Static Memory Allocation

For static variables, such as int a or char str[10], memory management is automatic—allocated and deallocated by the compiler. Conversely, for dynamically allocated memory (e.g., int *p = new int[10]), the programmer is responsible for deallocation. Failure to do so can lead to memory leaks, where the memory remains allocated until the program terminates.

Memory Management in C++

C uses functions like malloc() and calloc() for dynamic memory allocation, along with free() for deallocation. C++, however, offers a more streamlined approach using the new and delete operators.

Using the new Operator

The new operator requests memory allocation from the heap. If successful, it returns a pointer to the allocated memory.

Syntax :

				
					pointer-variable = new data-type;

				
			

Example:

				
					#include <iostream>
using namespace std;

int main() {
    // Pointer to store the address of allocated memory
    int* ptr = new int; // Allocates memory for an integer
    *ptr = 10;          // Assigns a value to the allocated memory

    // Printing the address and value
    cout << "Address: " << ptr << endl;
    cout << "Value: " << *ptr << endl;

    delete ptr; // Deallocating the memory
    return 0;
}

				
			
Initializing Dynamically Allocated Memory

You can also initialize memory directly upon allocation:

Example:

				
					#include <iostream>
using namespace std;

struct CustomType {
    int value;
    CustomType(int v) : value(v) {}
};

int main() {
    int* p = new int(42); // Initializes an integer with 42
    CustomType* obj = new CustomType(100); // Initializes CustomType

    cout << *p << " " << obj->value << endl;

    delete p;     // Deallocate integer
    delete obj;   // Deallocate CustomType
    return 0;
}

				
			

Output:

				
					a + b = 30
a - b = 20
a * b = 125
a / b = 5
a % b = 0
+a = 25
-a = -25
a++ = 25
a-- = 26

				
			

Allocating Arrays : You can allocate memory for an array using the new operator:

Example:

				
					#include <iostream>
using namespace std;

int main() {
    int n = 5;
    int* array = new int[n]; // Allocates memory for an array of 5 integers

    for (int i = 0; i < n; ++i) {
        array[i] = i * 2; // Initializing array elements
    }

    // Displaying the values
    for (int i = 0; i < n; ++i) {
        cout << array[i] << " "; // Outputs: 0 2 4 6 8
    }
    
    delete[] array; // Deallocate the array
    return 0;
}

				
			

Output:

				
					a < b  : 0
a > b  : 1
a <= b: 0
a >= b: 1
a == b: 0
a != b : 1

				
			

Handling Memory Allocation Failures : When memory allocation fails, the new operator throws a std::bad_alloc exception unless nothrow is used:

Example:

				
					#include <iostream>
using namespace std;

int main() {
    int* p = new(nothrow) int; // Attempt to allocate memory
    if (!p) {
        cout << "Memory allocation failed\n";
    } else {
        *p = 42;
        cout << "Value: " << *p << endl;
        delete p; // Deallocate memory
    }
    return 0;
}

				
			

Output:

				
					a && b : 1
a || b : 1
!a: 0
				
			

The delete Operator: To free memory allocated with new, use the delete operator:

Syntax:

				
					delete pointer-variable;        // For single objects
delete[] pointer-variable;      // For arrays

				
			

Example:

				
					#include <iostream>
using namespace std;

int main() {
    int* singleInt = new int(5);
    float* floatValue = new float(10.5);
    
    cout << "Integer: " << *singleInt << endl;
    cout << "Float: " << *floatValue << endl;

    delete singleInt; // Freeing single integer memory
    delete floatValue; // Freeing float memory

    int n = 3;
    int* intArray = new int[n]; // Allocating an array
    for (int i = 0; i < n; ++i) {
        intArray[i] = i + 1;
    }

    cout << "Array values: ";
    for (int i = 0; i < n; ++i) {
        cout << intArray[i] << " "; // Outputs: 1 2 3
    }

    delete[] intArray; // Freeing the entire array
    return 0;
}

				
			

In C++, memory can be dynamically allocated using the new and delete operators, while C and C++ also provide the malloc() and free() functions for similar purposes. Though they seem to serve the same function, there are key differences between them.

				
					#include <iostream>
using namespace std;

// Class A
class A {
    int a;
public:
    int* ptr;
    
    // Constructor of class A
    A() {
        cout << "Constructor was Called!" << endl;
    }
};

// Driver Code
int main() {
    // Creating an object of class A using new
    A* a = new A;
    cout << "Object of class A was created using new operator!" << endl;
    
    // Creating an object of class A using malloc
    A* b = (A*)malloc(sizeof(A));
    cout << "Object of class A was created using malloc()!" << endl;

    // Cleanup
    delete a;
    free(b); // Note: This does not call the destructor
    return 0;
}

				
			

Output:

				
					Constructor was Called!
Object of class A was created using new operator!
Object of class A was created using malloc()!

				
			

new vs malloc() and free() vs delete in C++

Differences between new/delete and malloc()/free() in C++

In C++, new and delete operators are used for dynamic memory allocation and deallocation, similar to malloc() and free() in C. Although they perform similar tasks, there are key differences between them, particularly in how they handle constructors and destructors.

Key Differences: Constructors and Destructors
  • malloc(): This is a C library function that can also be used in C++. However, it doesn’t invoke the constructor of a class when allocating memory for an object.
  • new: The new operator, which is exclusive to C++, allocates memory and also calls the constructor for object initialization.

Below is an example illustrating the difference between new and malloc():

				
					// C++ program to demonstrate `new` vs `malloc()`
#include <iostream>
using namespace std;

// Class Example
class Example {
    int value;

public:
    int* pointer;

    // Constructor
    Example() {
        cout << "Constructor called!" << endl;
    }
};

int main() {
    // Allocating memory with `new`
    Example* obj1 = new Example;
    cout << "Object created using `new`!" << endl;

    // Allocating memory with `malloc`
    Example* obj2 = (Example*)malloc(sizeof(Example));
    cout << "Object created using `malloc()`!" << endl;

    return 0;
}

				
			

Output:

				
					Constructor called!
Object created using `new`!
Object created using `malloc()`!

				
			

In this example, when using new, the constructor is called, whereas with malloc, it is not.

free() vs delete
  • free(): A C library function that deallocates memory but does not call the destructor of a class.
  • delete: A C++ operator that not only deallocates memory but also calls the destructor of the class to handle cleanup.

Example:

				
					// C++ program to demonstrate `free()` vs `delete`
#include <iostream>
using namespace std;

// Class Example
class Example {
    int value;

public:
    int* pointer;

    // Constructor
    Example() {
        cout << "Constructor called!" << endl;
    }

    // Destructor
    ~Example() {
        cout << "Destructor called!" << endl;
    }
};

int main() {
    // Using `new` and `delete`
    Example* obj1 = new Example;
    cout << "Object created using `new`!" << endl;
    delete obj1;
    cout << "Object deleted using `delete`!" << endl;

    // Using `malloc` and `free`
    Example* obj2 = (Example*)malloc(sizeof(Example));
    cout << "Object created using `malloc()`!" << endl;
    free(obj2);
    cout << "Object deleted using `free()`!" << endl;

    return 0;
}

				
			

Output:

				
					Constructor called!
Object created using `new`!
Destructor called!
Object deleted using `delete`!

Object created using `malloc()`!
Object deleted using `free()`!

				
			

In this output, when using delete, the destructor is called, but with free, it is not.

More Examples for Understanding

Example 1: Demonstrating Automatic Destructor Call

In this example, we show that the destructor is called automatically when an object goes out of scope, even without explicitly using delete.

				
					// C++ program to demonstrate automatic destructor call
#include <iostream>
using namespace std;

// Class Example
class Example {
    int value;

public:
    int* pointer;

    // Constructor
    Example() {
        cout << "Constructor called!" << endl;
    }

    // Destructor
    ~Example() {
        cout << "Destructor called!" << endl;
    }
};

int main() {
    Example obj;
    return 0;
}

				
			

Output:

				
					Constructor called!
Destructor called!

				
			

Here, the destructor is called automatically when return 0 is executed, signaling the end of the program.

To prevent the automatic destructor call, you could replace return 0 with exit(0), which terminates the program immediately. Here’s an example of how that would work:

Example 2: Preventing Automatic Destructor Call with exit(0)

				
					// C++ program to prevent automatic destructor call
#include <iostream>
using namespace std;

// Class Example
class Example {
    int value;

public:
    int* pointer;

    // Constructor
    Example() {
        cout << "Constructor called!" << endl;
    }

    // Destructor
    ~Example() {
        cout << "Destructor called!" << endl;
    }
};

int main() {
    Example obj;
    exit(0);
}

				
			

Output:

				
					Constructor called!

				
			

Here, the destructor is not called because exit(0) halts the program immediately.

Example 3: Dynamically Allocating an Object with new

				
					// C++ program to demonstrate `new` operator
#include <iostream>
using namespace std;

// Class Example
class Example {
    int value;

public:
    int* pointer;

    // Constructor
    Example() {
        cout << "Constructor called!" << endl;
    }

    // Destructor
    ~Example() {
        cout << "Destructor called!" << endl;
    }
};

int main() {
    // Dynamically creating an object
    Example* obj = new Example;
    return 0;
}

				
			

Output:

				
					Constructor called!