Programming in C++ is flexible and supports procedural and object-oriented paradigms. Pointers are strong tools in C++ programming that enable effective memory management and versatile data manipulation. Pointers become considerably more flexible when paired with objects, allowing for polymorphism, dynamic object generation, and intricate data structures. Programming in C++ effectively requires an understanding of pointers to objects, particularly when working with dynamic memory allocation and polymorphism.
In this article, we will learn the concept of pointer to object in C++, provide a thorough explanation, and provide useful examples to assist you in learning this crucial programming skill.
What are Pointers in C++?
Pointers are the variables that store another variable’s memory address. Pointers are addresses represented symbolically. They can point to any form of data type in C++, including floats, integers, and objects, and are specified using the * operator. They let programs generate and work with dynamic data structures, and they also let them imitate call-by-reference. One of the primary uses of pointers is iterating over elements in arrays or other data structures.
Syntax:
datatype var_name = value;
int *ptr_name = &var_name;
Or
int n = 30;
int *ptr_name = &n; // ptr is a pointer to the integer variable ‘n’
In the given syntax, ‘ptr’ holds the variable ‘n’ address. Pointers are useful for direct memory access and are a fundamental concept in C++.
Example:
#include <bits/stdc++.h>
using namespace std;
void testFunction()
{
// declare a variable n
int n = 30;
// declare pointer variable
int* ptr = &n; // data type of ptr and var must be same
//Assign the address of a variable to a pointer
cout << "The value at ptr = " << ptr << "n";
cout << "The value at n = " << n << "n";
cout << "The value at *ptr = " << *ptr << "n";
}
// Main program
int main()
{
testFunction();
return 0;
}
Output:
The value at ptr = 0x7fff3293fcb4
The value at n = 30
The value at *ptr = 30
Get curriculum highlights, career paths, industry insights and accelerate your technology journey.
Download brochure
What are Objects in C++?
An instance of a class is called an object in C++. Classes are user-defined data types that can hold member functions (methods) and data members (variables). Programming encapsulation and abstraction are made possible by objects.
Syntax:
Classname obj_name;
Example:
class Vehicle {
public:
string maker;
int year;
void show() {
cout << "Maker of car is: " << maker << ", and Year is: " << year << endl;
}
};
Vehicle car; // ‘car’ is an object of class ‘Vehicle’
In this example, the car is the object of the Vehicle class, having its data members as year and maker.
What is Pointer to Object in C++?
A pointer to an object is a pointer that holds an object’s address. It is a variable that stores an object’s memory address. The class name is used as the datatype, although it is stated similarly to a pointer to any other data type. It gives one a method to manipulate and dynamically interact with the item by granting indirect access to it. When working with dynamically allocated memory, pointers to objects are very helpful since they allow for memory management.
An object is created for a class and takes up a certain amount of memory. Instead of storing the actual object in memory, a pointer to an object in C++ stores the memory address of an object.
Syntax:
ClassName *pointerName;
where,
ClassName: The name of the class.
*: Indicates that it is a pointer.
pointerName: The name of the pointer variable.
The address can be stored in the pointer to the object using the above syntax.
And for storing the address of an object into a pointer in C++:
pointerName = &objname;
Declaration And Use Of Object Pointers In C++
To create a pointer to an object, you first need to declare an object and then assign its address to the pointer. Let’s see how:
Vehicle veh; // Declare an object of class ‘Vehicle’
Vehicle *vehPtr = &veh; // Declare a pointer to object ‘veh’
Currently, the object veh’s address is stored in vehPtr. With the -> operator, you may use this pointer to access the members of veh object.
Accessing Object Members using Pointers
The arrow operator (->) can be used to access an object’s members when you have a pointer to it. See the below example:
vehPtr->type = “Car”; // Setting the vehicle type to “Car”
vehPtr->maker = “BMW”; // Setting the vehicle manufacturer to “BMW”
vehPtr->year = 2024; // Setting the manufacturing year to 2024
vehPtr->show();
The above example will set the type, maker, and year of the ‘veh’ object and then call the show() function. To have a better understanding, see the complete example:
Example:
#include <iostream>
using namespace std;
// Declare a class named Vehicle
class Vehicle {
public:
// Public member variables
string type; // Stores the type of vehicle (e.g., Car, Bike, etc.)
string maker; // Stores the manufacturer of the vehicle
int year; // Stores the manufacturing year of the vehicle
// Member function to display the vehicle's information
void show() {
cout << "The vehicle type is a " << type
<< ", built by " << maker
<< " and Year is " << year << endl;
}
};
int main() {
// Create an object of the Vehicle class
Vehicle veh;
// Declare a pointer to the Vehicle object
Vehicle *vehPtr = &veh;
// Use the pointer to set the values of the Vehicle object's members
vehPtr->type = "Car"; // Setting the vehicle type to "Car"
vehPtr->maker = "BMW"; // Setting the vehicle manufacturer to "BMW"
vehPtr->year = 2024; // Setting the manufacturing year to 2024
// Call the show() function using the pointer to display the vehicle's information
vehPtr->show(); // Outputs: The vehicle type is a Car, built by BMW and Year is 2024
return 0; // Return 0 to indicate successful execution
}
Output:
The vehicle type is a Car, built by BMW and Year is 2024
Explanation:
Each member variable of the class Vehicle is described with its purpose in the example.
The show() function is used to display the vehicle’s details.
The pointer vehPtr is used to access and modify the members of the Vehicle object veh.
The arrow operator (->) is used to access members of the object through the pointer.
Dynamic Memory Allocation for Objects
Dynamic memory allocation enables a programmer to allocate memory space for objects at runtime. It is useful when the number of objects to be created is not known at compile time or when you want to dynamically allocate resources based on user input plus file data and other run-time causes. With the new keyword, memory for objects can be allocated on the heap (also known as the free store). Contrary to the stack, memory allocated here in the heap remains allocated until explicitly deallocated with the use of the delete keyword. This is more of providing more control though it can introduce memory leaks if not careful.
Why use dynamic memory allocation?
Unknown Object Count at Compile Time: If the required number of objects is not known at compile time, dynamic memory allocation will allow you to create as many objects as needed without predefining the count.
Larger Memory Capacity: A stack may be of limited size, but a heap can be much larger.
Lifetimes Beyond Scope: Objects created dynamically can persist beyond the scope of the function or block where they were created.
Syntax:
ClassName *ptr = new ClassName; // Allocates memory for a single object ClassName *ptrArray = new ClassName[size]; // Allocates memory for an array of objects where,
new ClassName: It allocates memory for a single object of ClassName and returns a pointer to it.
new ClassName[size]: It allocates memory for an array of size objects of ClassName and returns a pointer to the first element of the array.
Example:
#include <iostream>
#include <string>
using namespace std;
// Define the Vehicle class
class Vehicle {
public:
string type;
int year;
float mileage;
// Constructor
Vehicle(string n, int a, float g) : type(n), year(a), mileage(g) {}
// Default Constructor
Vehicle() : type("Unknown"), year(0), mileage(0.0f) {}
// Member function to display vehicle information
void showInfo() {
cout << "Vehicle type is: " << type << ", Year: " << year << ", Mileage: " << mileage << endl;
}
};
int main() {
int numVehicles;
cout << "Enter the number of vehicles: ";
cin >> numVehicles;
// Dynamically allocate memory for an array of Vehicle objects
Vehicle *vehicle = new Vehicle[numVehicles];
// Input data for each vehicles
for (int i = 0; i < numVehicles; i++) {
cout << "Enter details for student " << i + 1 << " (type year mileage): ";
cin >> vehicle[i].type >> vehicle[i].year >> vehicle[i].mileage;
}
// Display information for all vehicles
cout << "nVehicle Information:n";
for (int i = 0; i < numVehicles; i++) {
vehicle[i].showInfo();
}
// Deallocate memory to prevent memory leaks
delete[] vehicle;
return 0;
}
Output:
Enter the number of vehicles: 1
Enter details for student 1 (type year mileage): car 2024 32.4
Vehicle Information:
Vehicle type is: car, Year: 2024, Mileage: 32.4
Explanation:
The Vehicle class represents a vehicle with attributes like type, year, and mileage. It has two constructors: a parameterised constructor to initialise these attributes with user-defined values and a default constructor that initialises the attributes to default values (“Unknown”, 0, 0.0f). The class also includes a member function, showInfo(), which displays the details of the vehicle class objects.
In the main() function, the number of vehicles is first input by the user. Then, a dynamic array of Vehicle objects is created using the new keyword: Vehicle *vehicle = new Vehicle[numVehicles]. This array will hold the details of each vehicle, and memory is allocated on the heap to handle the number of vehicles specified by the user.
A loop is used to collect data for each vehicle. The user is prompted to enter details like type, year, and mileage for each vehicle object in the array. The input is stored directly in the dynamically allocated array vehicle using the subscript operator [].
Another loop iterates over the array of Vehicle objects and calls the showInfo() function for each object. This function prints the type, year, and mileage of each vehicle, showcasing the data stored in the objects.
After all operations are complete, the dynamically allocated memory is released using the delete[] vehicle. It is crucial to prevent memory leaks, as failing to deallocate dynamically allocated memory can lead to increased memory usage and potential crashes.
Accessing Object Members Through Pointers
There are two ways to access object members through pointers:
1. Arrow Operator (->): This is the most common and convenient way.
Syntax: carPtr->type = “BMW”;
2. Dereference Operator (*) and Dot Operator (.): This method involves dereferencing the pointer first.
Syntax: (*carPtr).type = “BMW”; Both methods are equivalent, but the arrow operator is generally preferred for its clarity and conciseness.
Base Class Pointers to Derived Class Objects
Pointers to objects are especially handy when dealing with inheritance. An object of any class descended from a base class can be accessed by a pointer of that base class type. This concept is fundamental for achieving runtime polymorphism and using features like virtual functions.
Inheritance Polymorphism: A derived class object can access base class members when a base class pointer references it. The derived class’s version of a virtual function that is overridden in the base class is called if it exists in the derived class.
Dynamic Binding: Virtual functions are used to enable dynamic binding in C++. Runtime determines which function (derived or base) to use based on the actual object type that the pointer references.
Use Cases: This technique is also frequently applied in the creation of generic algorithms, managing collections of objects of various derived types, and interface implementation.
Syntax:
The basic syntax for pointers to derived class objects is as follows:
BaseClass* pointerName = new DerivedClass();
Example:
#include<iostream>
using namespace std;
// Parent class
class Vehicle {
public:
virtual void start() {
cout << "Vehicle starting..." << endl;
}
};
// Derived Car class
class Car : public Vehicle {
public:
void start() override {
cout << "Car engine starting..." << endl;
}
};
// Derived Bicycle class
class Bicycle : public Vehicle {
public:
void start() override {
cout << "Bicycle is ready..." << endl;
}
};
// Main driver
int main() {
Vehicle* vPtr; // declare the pointer
Car myCar;
Bicycle myBike;
vPtr = &myCar;
vPtr->start();
vPtr = &myBike;
vPtr->start();
return 0;
}
Output:
Car engine starting...
Bicycle is ready…
Explanation:
This example demonstrates polymorphism in C++ using a pointer to an object. The Vehicle base class has a virtual start() function, overridden by the derived Car and Bicycle classes. A base class pointer vPtr can point to objects of any derived class. When vPtr points to a Car object (myCar), calling vPtr->start() invokes the Car class’s start() method, printing “Car engine starting…”. When vPtr points to a Bicycle object (myBike), it calls the Bicycle class’s start() method, printing “Bicycle is ready…”. This shows dynamic binding, allowing a single pointer to call different methods based on the actual object type.
Array of Pointers to Objects
An array of pointers to objects can also be made, which is helpful for managing several objects at once. In C++ or any other programming language, an array’s indices can be used to randomly retrieve a collection of related data objects that are kept in contiguous memory locations. Basic data types like int, float, double, and char can be stored in arrays.
Syntax:
Classname *obj_name[n]; // Array of pointers to ‘Classname’ objects
Example:
#include <iostream>
#include <string>
using namespace std;
// Car class definition
class Car {
public:
string model; // Car model
// Constructor to initialise the car model
Car(string m = "") : model(m) {}
// Method to display the car model
void show() {
cout << "Car model: " << model << endl;
}
};
int main() {
// Array of pointers to 'Car' objects
Car* carArray[3];
// Dynamically allocate memory for each Car object in the array
carArray[0] = new Car;
carArray[1] = new Car;
carArray[2] = new Car;
// Assign car models to each object
carArray[0]->model = "BMW";
carArray[1]->model = "Audi";
carArray[2]->model = "Mercedes";
// Display the models of each car using the show() method
for (int i = 0; i < 3; i++) {
carArray[i]->show();
}
// Deallocate memory for each Car object in the array
for (int i = 0; i < 3; i++) {
delete carArray[i];
}
return 0;
}
Output:
Car model: BMW
Car model: Audi
Car model: Mercedes
Explanation:
In this example, the Car class is defined with a public member model to store the car name and a member function show() to print the car model.
An array of pointers, carArray[3], is declared to hold pointers to Car objects. Memory for each Car object is allocated using a new Car.
Each Car object in the array is then assigned a model value, and then show() is called to display the model.
Finally, the dynamically allocated memory for each Car object is deallocated using delete to avoid memory leaks.
Advantages of Pointer to Object in C++
There are various advantages or benefits of working with the pointer to an object in C++.
Here are some of them:
Dynamic Memory Management: This is achieved by allowing pointers to objects; hence, memory can be dynamically allocated and deallocated during run time using new and delete. Objects may be created on the heap with this capability, hence providing greater flexibility of memory usage management and object lifetimes, i.e., not like in a situation where objects’ numbers are known in advance.
Pass by reference: Pointers to objects let us pass objects to functions by reference, which can be more efficient than passing by value. An object is copied when we offer it by value, which might take a long time and use a lot of memory for large objects.
Efficient Polymorphism: This is where pointers come in and are quite handy for achieving runtime polymorphism. When you have pointers of a base class pointing to the objects of a derived class, you have dynamic binding which ensures the correct overridden function is called during runtime. It is very useful, especially in implementing interfaces and abstract classes.
Handling Arrays of Objects: You can dynamically generate arrays of objects by using pointers. This enables effective administration and manipulation of object collections and is helpful when the size of the array is unknown at compile time.
Smart Pointers: Ownership semantics for dynamically allocated objects are provided by smart pointers. These useful programming tools allow you to control an object’s lifespan and make sure it is deallocated appropriately when it is no longer needed.
Object Sharing and Manipulation: Pointers enable sharing as well as manipulation of the same object by multiple portions of the program without creating copies. This is accomplished by passing only the pointer when large objects are involved so that copying overhead can be avoided.
Common Pitfalls and Best Practices
Here are some common pitfalls and best practices that one should look into while working with pointers to objects in C++:
Null pointer: A common mistake is not checking if a pointer is nullptr before dereferencing. So, always check if a pointer is null before dereferencing it.
if (carPtr != nullptr) {carPtr->displayInfo();
}
Memory Leaks: Another mistake is not deallocating the allocated memory which gets the memory leak issue. So, always deallocate the memory to prevent leakage:
Car* carPtr = new Car();// … use carPtr …
delete carPtr;
carPtr = nullptr; // Set to nullptr after deleting
Dangling Pointers: Trying to access those pointers that have been deallocated already is another mistake.
Car* carPtr1 = new Car();Car* carPtr2 = carPtr1;
delete carPtr1; // carPtr2 is now a dangling pointer carPtr1 = nullptr; carPtr2 = nullptr; // Also set carPtr2 to nullptr
Const correctness: Use const pointers when the pointed-to object should not be modified.
const Car* constCarPtr = &myCar;
Using smart pointers: Consider using smart pointers like std::unique_ptr and std::shared_ptr for automatic memory management.
#include <memory>
std::unique_ptr<Car> carPtr = std::make_unique<Car>();
carPtr->brand = "Tesla";
carPtr->displayInfo();
// No need to manually delete
Conclusion
Pointers to objects are a powerful feature of C++ that enable efficient memory management and dynamic behaviour through polymorphism. The pointer variable in C++ is used to hold and control object memory addresses, allowing for indirect access to those objects. Writing reliable and effective C++ programs requires knowing how to use pointers to objects, including managing polymorphism and dynamic memory allocation. By following best practices and avoiding common pitfalls, you can harness the full potential of pointers to objects in C++.
In this article, we have covered syntax, pointers, objects, declaration & accessing object members, etc., along with detailed examples of pointers to objects. Now that you know about pointer to object, you can utilise pointers to handle objects in your C++ projects with confidence.
FAQs
How Do You Declare a Pointer to an Object in C++?
A pointer to an object in C++ is declared like any other pointer,i.e., we use the * symbol after the data type. This indicates that it is pointing to an object. It should enable you to dynamically create and manipulate objects and access object members using the -> operator. Also, it should permit polymorphic behaviour when pointing to derived class objects through base class pointers.
How does polymorphism work with pointers to objects?
Polymorphism works with pointers to objects by having a base class pointer point to a derived class object. In case the base class has virtual functions, calling these functions through the pointer will make them get resolved to the overridden function in the derived class.
What are the advantages of object pointers?
Advantages of pointers to objects include reduced overhead when providing large objects to functions, effective polymorphism implementation, and dynamic memory management. They are also necessary for building intricate data structures like trees and linked lists, as well as for enabling improved resource management through the use of RAII techniques.
Can a pointer point to another pointer in C++?
Yes, a pointer can point to another pointer in C++. The concept is known as "pointer to a pointer". Multiple levels of indirection are supported, allowing you to access and change a pointer's value by using another pointer. When working with class hierarchies in C++, a pointer to a base class or even a pointer to another pointer can contain the location of a pointer to the base class. This method works well in situations involving multi-dimensional arrays, dynamic memory allocation, and sending pointers to methods that require altering the original pointer.
Example:
#include <iostream>
using namespace std;
int main() {
int n = 35;
int* ptr1 = &n; // Pointer to an int
int** ptr2 = &ptr1; // Pointer to another pointer (ptr1)
cout << "Value: " << n << endl; // Output: 35
cout << "Value via ptr1: " << *ptr1 << endl; // Output: 35
cout << "Value via ptr2: " << **ptr2 << endl; // Output: 35
return 0;
}
What are the Different Types of Pointers in C++?
C++ offers various types of pointers, each serving unique purposes. Here’s an overview of the most commonly used types:
Null Pointer: When initialised with nullptr or NULL, a null pointer does not point to any address in legal memory. It's frequently used to show that a pointer has no assigned value to any data or object.
Dangling Pointer: A dangling pointer points to a memory location that has been freed or deleted. Accessing or modifying this pointer can lead to undefined behaviour and potential program crashes.
Void Pointer: A void pointer (void*) can point to any data type, but cannot be dereferenced directly. It’s typically used for generic data storage and requires casting to the appropriate data type before use.
Wild Pointer: An uninitialised pointer that links to any random location in memory is called a wild pointer. If accessed without doing the necessary setup, it may result in unexpected behaviour and issues.
Smart Pointer: Smart pointers are C++ classes that control the lifespan of dynamically allocated objects. Examples of these classes are std::unique_ptr and std::shared_ptr. They automatically deallocate memory when it's no longer in use, helping to prevent memory leaks and dangling pointer problems.
Hero Vired is a leading LearnTech company dedicated to offering cutting-edge programs in collaboration with top-tier global institutions. As part of the esteemed Hero Group, we are committed to revolutionizing the skill development landscape in India. Our programs, delivered by industry experts, are designed to empower professionals and students with the skills they need to thrive in today’s competitive job market.