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:
Program
|
new int
|
↓
Dynamic Memory
(Heap)
|
┌───────┐
│ 50 │
└───────┘
↑
|
pHere, 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:
Memory is allocated for an
int.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 = 1003. 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:
Studentis the class.ptris a pointer to aStudent.new Studentdynamically creates aStudentobject.
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: 101The 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: 103The array can be visualized as:
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: 103The 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->rollNois equivalent to:
(*ptr).rollNoThe 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: 501Here:
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:
| Object | Object Pointer |
|---|---|
Student s; | Student *ptr; |
| Represents an object | Stores 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
MemoryThe 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_casttypeid
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:
Object s
┌─────────────┐
│ rollNo │
│ marks │
└─────────────┘
↑
|
this pointer
|
member functionThus, 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: 10125. 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 parameterTherefore, 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 → s2Therefore, 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:
Is available inside non-static member functions.
Points to the current object.
Helps distinguish data members from parameters/local variables with the same name.
Can be used to access current object's members.
Can be used to return the current object using
*this.Is not available in a static member function because a static member function is not associated with a particular object.
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.
| Operator | Purpose |
|---|---|
new | Allocates one object |
delete | Releases 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: 103This single example demonstrates:
Class
Objects
Dynamic allocation
Array of objects
Object pointers
new[]delete[]Member-function calls
thispointer
31. Common Errors
Error 1: Using delete instead of delete[]
Student *s = new Student[5];
delete s; // IncorrectCorrect:
delete[] s;Error 2: Dereferencing a deleted pointer
Student *s = new Student;
delete s;
s->display(); // InvalidAfter 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:
sis the object.pis 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 ObjectCore concepts to remember:
new→ dynamically createdelete→ dynamically destroy one objectnew[]→ dynamically create an arraydelete[]→ destroy a dynamically allocated array->→ access members through an object pointertypeid→ obtain runtime type informationdynamic_cast→ checked runtime cast in polymorphic hierarchiesthis→ pointer to the current object