Programming Pandit

c/c++/c#/Javav/Python


Latest Update

Monday, September 14, 2026

Arrays of Objects, Pointers to Objects, Type Checking, this Pointer

 

The Dynamic Allocation Operators: Arrays of Objects, Pointers to Objects, Type Checking, this Pointer

Dynamic memory allocation is an important feature of C++ that allows memory to be allocated during program execution rather than being fixed completely at compile time. This is particularly useful in object-oriented programming when the number of objects required by a program is known only at runtime.

C++ provides two important operators for dynamic memory management:

  • new — allocates memory dynamically.

  • delete — releases dynamically allocated memory.

For arrays, C++ provides:

  • new[] — dynamically allocates an array.

  • delete[] — releases a dynamically allocated array.

Dynamic allocation becomes especially useful when working with arrays of objects, pointers to objects, and dynamically created objects.


1. Dynamic Allocation Operators

In C++, memory can be allocated dynamically using the new operator.

Syntax

pointer = new data_type;

For example:

int *p;

p = new int;

The new operator allocates memory for an integer and returns the address of that memory location.

A value can then be assigned using the pointer:

*p = 50;

The memory should eventually be released using delete:

delete p;

The basic process can be represented as:

Image

Image

Image

Image

Image

Image

              Program
                 |
              new int
                 |
                 ↓
          Dynamic Memory
              (Heap)
                 |
             ┌───────┐
             │  50   │
             └───────┘
                 ↑
                 |
                 p

Here, p stores the address of the dynamically allocated integer.


2. new Operator

The new operator performs dynamic memory allocation and returns a pointer to the allocated object.

For example:

int *p = new int;

The statement performs two operations:

  1. Memory is allocated for an int.

  2. The address of the allocated memory is stored in p.

A value can be assigned as:

*p = 100;

Complete Example

#include <iostream>
using namespace std;

int main()
{
    int *p = new int;

    *p = 100;

    cout << "Value = " << *p << endl;

    delete p;

    return 0;
}

Output

Value = 100

3. delete Operator

The delete operator releases memory that was previously allocated using new.

Syntax

delete pointer;

Example:

int *p = new int;

*p = 50;

delete p;

After the memory has been released, the pointer should not be dereferenced.

A useful practice is to make the pointer null after deletion:

delete p;
p = nullptr;

This helps avoid accidentally using a pointer that no longer refers to a valid dynamically allocated object.


4. Dynamic Allocation of Objects

The new operator can also dynamically create an object.

Suppose we have:

class Student
{
public:
    void display()
    {
        cout << "Student object";
    }
};

An object can be dynamically created using:

Student *ptr = new Student;

Here:

  • Student is the class.

  • ptr is a pointer to a Student.

  • new Student dynamically creates a Student object.

The member function can be accessed using the arrow (->) operator:

ptr->display();

The object is released using:

delete ptr;

5. Example: Pointer to a Dynamically Allocated Object

#include <iostream>
using namespace std;

class Student
{
public:
    int rollNo;

    void display()
    {
        cout << "Roll Number: " << rollNo << endl;
    }
};

int main()
{
    Student *ptr = new Student;

    ptr->rollNo = 101;
    ptr->display();

    delete ptr;

    return 0;
}

Output

Roll Number: 101

The statement:

Student *ptr = new Student;

creates the object dynamically.

The statement:

ptr->display();

calls the member function through the pointer.


6. Arrays of Objects

In object-oriented programming, it is often necessary to create multiple objects of the same class.

For example, a university may need to store information about 100 students.

Instead of creating individual objects:

Student s1, s2, s3, ...;

an array of objects can be created:

Student students[100];

An array of objects stores multiple objects of the same class in a contiguous sequence of elements, subject to the usual object and array rules.


7. Example of Array of Objects

#include <iostream>
using namespace std;

class Student
{
public:
    int rollNo;

    void display()
    {
        cout << "Roll Number: " << rollNo << endl;
    }
};

int main()
{
    Student s[3];

    s[0].rollNo = 101;
    s[1].rollNo = 102;
    s[2].rollNo = 103;

    s[0].display();
    s[1].display();
    s[2].display();

    return 0;
}

Output

Roll Number: 101
Roll Number: 102
Roll Number: 103

The array can be visualized as:

Image

Image

Image

Image

Image

Student s[3]

┌─────────────┬─────────────┬─────────────┐
│   s[0]      │   s[1]      │   s[2]      │
│ Roll = 101  │ Roll = 102  │ Roll = 103  │
└─────────────┴─────────────┴─────────────┘

Each array element is a separate object of the Student class.


8. Dynamically Allocated Array of Objects

An array of objects can also be created dynamically using new[].

Syntax

ClassName *pointer = new ClassName[size];

For example:

Student *students = new Student[5];

This dynamically allocates an array containing five Student objects.

The dynamically allocated array must be released using delete[]:

delete[] students;

It is important to use delete[] for memory allocated with new[].


9. Example: Dynamic Array of Objects

#include <iostream>
using namespace std;

class Student
{
public:
    int rollNo;

    void display()
    {
        cout << "Roll Number: " << rollNo << endl;
    }
};

int main()
{
    Student *students = new Student[3];

    students[0].rollNo = 101;
    students[1].rollNo = 102;
    students[2].rollNo = 103;

    for (int i = 0; i < 3; i++)
    {
        students[i].display();
    }

    delete[] students;

    return 0;
}

Output

Roll Number: 101
Roll Number: 102
Roll Number: 103

The memory arrangement can be understood as:

                students
                    |
                    ↓
        Dynamic Memory (Heap)
        ┌────────┬────────┬────────┐
        │Student │Student │Student │
        │  [0]   │  [1]   │  [2]   │
        └────────┴────────┴────────┘
                    |
                 delete[]

10. Constructors with Dynamic Arrays of Objects

When objects are dynamically allocated using new, their constructors are automatically invoked.

Consider:

class Student
{
public:
    Student()
    {
        cout << "Constructor called" << endl;
    }
};

Now:

Student *s = new Student[3];

causes the constructor to execute for each of the three objects.

Similarly:

delete[] s;

causes the destructor to execute for each object, in the appropriate destruction order.

This is one of the important advantages of C++ object-oriented memory management.


11. Pointers to Objects

A pointer that stores the address of an object is called a pointer to an object.

Syntax

ClassName *pointer;

Example:

Student *ptr;

An object can be associated with the pointer:

Student s;

ptr = &s;

Now ptr points to object s.


12. Accessing Object Members Through a Pointer

There are two ways to access a member through an object pointer.

Using (*pointer).member

(*ptr).display();

Using the arrow operator ->

ptr->display();

Both represent the same member access.

For example:

ptr->rollNo

is equivalent to:

(*ptr).rollNo

The arrow operator is generally more convenient and readable.


13. Example: Pointer to Object

#include <iostream>
using namespace std;

class Employee
{
public:
    int id;

    void display()
    {
        cout << "Employee ID: " << id << endl;
    }
};

int main()
{
    Employee e;

    Employee *ptr = &e;

    ptr->id = 501;
    ptr->display();

    return 0;
}

Output

Employee ID: 501

Here:

Employee *ptr = &e;

makes ptr point to the object e.

Then:

ptr->id = 501;

accesses the id member through the pointer.


14. Object Pointer vs Normal Object

Consider:

Student s;

Here s is an object.

Now:

Student *ptr = &s;

Here ptr is a pointer to the object.

The distinction is:

ObjectObject Pointer
Student s;Student *ptr;
Represents an objectStores address of an object
Access member using .Access member using ->
s.display()ptr->display()

15. Pointer to Dynamically Created Object

A pointer can directly receive the address returned by new.

Student *ptr = new Student;

This is a very common pattern in C++.

The relationship is:

        ptr
         |
         | stores address
         ↓
   ┌─────────────┐
   │   Student   │
   │    Object   │
   └─────────────┘
      Dynamic
      Memory

The object can be accessed using:

ptr->display();

and released using:

delete ptr;

16. Type Checking

Type checking is the process of ensuring that an expression, object, pointer, or operation is used with a compatible data type.

C++ is a statically typed language, which means that types are primarily determined and checked at compile time.

For example:

int x = 10;

Here, x is declared as an int.

The compiler can detect invalid assignments such as:

x = "Hello";

because a string literal cannot be assigned directly to an int.

Type checking helps detect programming errors before the program executes.


17. Type Checking with Pointers and Objects

Type compatibility is particularly important when pointers are used.

For example:

class A
{
};

class B
{
};

A *p;
B *q;

The pointer p is intended to point to an object of type A, whereas q is intended to point to an object of type B.

An unrelated pointer conversion is not normally permitted without an explicit cast.

This provides a degree of compile-time safety.


18. Run-Time Type Information

C++ also provides Run-Time Type Information (RTTI) for determining the type of an object during program execution.

Two important mechanisms associated with RTTI are:

  • dynamic_cast

  • typeid

These are particularly useful when working with inheritance and polymorphism.


19. typeid Operator

The typeid operator can be used to obtain information about the type of an expression.

It requires:

#include <typeinfo>

Example:

#include <iostream>
#include <typeinfo>
using namespace std;

int main()
{
    int x = 10;

    cout << typeid(x).name();

    return 0;
}

The exact text returned by name() is implementation-dependent, so students should not assume that a particular compiler must print the same type code.


20. typeid with Classes

Consider:

#include <iostream>
#include <typeinfo>
using namespace std;

class Student
{
};

int main()
{
    Student s;

    cout << typeid(s).name();

    return 0;
}

typeid(s) provides a std::type_info object describing the type of s.

The exact representation returned by:

typeid(s).name()

depends on the compiler and implementation.

Therefore, typeid is useful for type identification, but name() should not generally be used when portable, human-readable type names are required.


21. dynamic_cast

dynamic_cast is mainly used with polymorphic class hierarchies to perform checked conversions at runtime.

Consider:

class Animal
{
public:
    virtual ~Animal() = default;
};

class Dog : public Animal
{
public:
    void bark()
    {
        cout << "Dog barks";
    }
};

Now:

Animal *ptr = new Dog;

Dog *dogPtr = dynamic_cast<Dog*>(ptr);

If ptr actually points to a Dog object, the conversion succeeds.

If it does not, a pointer dynamic_cast returns nullptr.

Example:

if (dogPtr != nullptr)
{
    dogPtr->bark();
}

This provides a runtime-checked downcast.


22. Why Type Checking Is Important

Type checking helps:

  • Detect incompatible operations.

  • Prevent many programming errors.

  • Improve program reliability.

  • Provide safer pointer conversions.

  • Support runtime type identification in polymorphic hierarchies.

  • Make object-oriented programs easier to maintain.


23. this Pointer

The this pointer is a special pointer available inside a non-static member function.

It points to the current object on which the member function is operating.

For example:

class Student
{
public:
    void display()
    {
        cout << this;
    }
};

When:

Student s;
s.display();

is executed, this refers to s.

Conceptually:

Image

Image

Image

                Object s
             ┌─────────────┐
             │ rollNo      │
             │ marks       │
             └─────────────┘
                    ↑
                    |
                this pointer
                    |
             member function

Thus, this provides the member function with a way to refer to the object that invoked it.


24. Example of this Pointer

#include <iostream>
using namespace std;

class Student
{
private:
    int rollNo;

public:
    void setRollNo(int rollNo)
    {
        this->rollNo = rollNo;
    }

    void display()
    {
        cout << "Roll Number: " << this->rollNo;
    }
};

int main()
{
    Student s;

    s.setRollNo(101);
    s.display();

    return 0;
}

Output

Roll Number: 101

25. Why this Is Required in the Above Example

The function has a parameter named rollNo:

void setRollNo(int rollNo)

The class also has a data member named rollNo:

int rollNo;

Inside the function:

this->rollNo = rollNo;

means:

this->rollNo    → data member of current object
rollNo          → function parameter

Therefore, the statement assigns the parameter value to the current object's data member.

Without this, the two names would refer to the parameter in that scope.


26. this Pointer with Multiple Objects

The same member function can be called by different objects.

Student s1;
Student s2;

s1.display();
s2.display();

When s1 calls the function, this refers to s1.

When s2 calls the function, this refers to s2.

Conceptually:

s1.display()                 s2.display()
      |                            |
      ↓                            ↓
 this → s1                    this → s2

Therefore, this allows the same member-function code to operate on the correct current object.


27. Returning the Current Object Using this

The this pointer can also be used to return the current object from a member function.

A common form is:

return *this;

For example:

class Number
{
private:
    int value;

public:
    Number& setValue(int value)
    {
        this->value = value;
        return *this;
    }

    void display()
    {
        cout << value;
    }
};

Now:

Number n;

n.setValue(10).display();

The setValue() function returns the current object by reference, allowing another member function to be called immediately.


28. Important Properties of this Pointer

The this pointer:

  1. Is available inside non-static member functions.

  2. Points to the current object.

  3. Helps distinguish data members from parameters/local variables with the same name.

  4. Can be used to access current object's members.

  5. Can be used to return the current object using *this.

  6. Is not available in a static member function because a static member function is not associated with a particular object.

  7. Its type depends on the class and the cv/ref qualification of the member function.


29. new, delete, new[] and delete[]

These operators should be clearly distinguished.

OperatorPurpose
newAllocates one object
deleteReleases one object allocated with new
new[]Allocates an array of objects/elements
delete[]Releases an array allocated with new[]

Correct pairing is important:

Student *p = new Student;
delete p;

and:

Student *p = new Student[10];
delete[] p;

Do not replace:

delete[] p;

with:

delete p;

when the memory was allocated using new[].


30. Complete Example Combining the Concepts

#include <iostream>
using namespace std;

class Student
{
private:
    int rollNo;

public:
    Student()
    {
        rollNo = 0;
    }

    void setRollNo(int rollNo)
    {
        this->rollNo = rollNo;
    }

    void display()
    {
        cout << "Roll Number: " << this->rollNo << endl;
    }
};

int main()
{
    Student *students = new Student[3];

    students[0].setRollNo(101);
    students[1].setRollNo(102);
    students[2].setRollNo(103);

    for (int i = 0; i < 3; i++)
    {
        students[i].display();
    }

    delete[] students;

    return 0;
}

Output

Roll Number: 101
Roll Number: 102
Roll Number: 103

This single example demonstrates:

  • Class

  • Objects

  • Dynamic allocation

  • Array of objects

  • Object pointers

  • new[]

  • delete[]

  • Member-function calls

  • this pointer


31. Common Errors

Error 1: Using delete instead of delete[]

Student *s = new Student[5];

delete s;       // Incorrect

Correct:

delete[] s;

Error 2: Dereferencing a deleted pointer

Student *s = new Student;

delete s;

s->display();   // Invalid

After deletion, the pointer no longer points to a valid object.

A useful practice is:

delete s;
s = nullptr;

Error 3: Accessing object members using the wrong operator

For an object:

Student s;

s.display();

For a pointer:

Student *p = &s;

p->display();

Error 4: Confusing the object with its pointer

Student s;
Student *p = &s;

Here:

  • s is the object.

  • p is the pointer to the object.


32. Summary

Dynamic allocation allows C++ programs to create objects and arrays whose lifetime and size are determined during execution. The new operator dynamically creates an object or array and returns its address, while delete and delete[] release the corresponding memory.

An array of objects allows multiple instances of the same class to be stored and processed systematically. When the size of the array is known only during execution, a dynamic array can be created using new[] and released using delete[].

A pointer to an object stores the address of an object and allows its members to be accessed using the arrow (->) operator. C++ also provides type checking and RTTI mechanisms, including typeid and dynamic_cast, which are particularly useful when dealing with polymorphic class hierarchies.

The this pointer represents the current object inside a non-static member function. It is particularly useful when a parameter has the same name as a data member and when a member function needs to return the current object.

Quick Revision

Dynamic Memory Allocation
          |
    ----------------
    |              |
   new            delete
    |              |
    ↓              ↓
Object/Array    Release memory
    |
    ├── new Object
    │      ↓
    │   Object Pointer
    │
    └── new[]
           ↓
     Array of Objects
           |
        delete[]
           
Type Checking
     |
     ├── Compile-time type checking
     └── RTTI
          ├── typeid
          └── dynamic_cast

this Pointer
     ↓
Current Object

Core concepts to remember:

new → dynamically create
delete → dynamically destroy one object
new[] → dynamically create an array
delete[] → destroy a dynamically allocated array
-> → access members through an object pointer
typeid → obtain runtime type information
dynamic_cast → checked runtime cast in polymorphic hierarchies
this → pointer to the current object

No comments:

Post a Comment