[C++] OOPs - Pointers to Classes, this Pointer, Static Members and Class
The Curious Case of this Pointer
"What happens when we call a function on an object? How does the object know it's itself?"
Let’s imagine we both work in a factory. Every machine is identical but has its own serial number. When a machine processes something, it must know which machine is doing it. Right? That’s exactly the case in C++. Every object has a way to refer to itself inside its own member functions — and that's through something called the this pointer.
But let’s not rush. First, let’s try a practical example.
Suppose we have two boxes. We want each box to compare its volume with another box and say, “Hey, I’m bigger” or “You’re bigger.”
But wait... if we write a compare() function, how does the box know who is it in that moment? That’s where the this pointer comes in.
What is a compare() function ?
Treat this as a black box for the time being. Just know that
the compare() function is a built-in member function of the std::string class, used to compare two strings. It returns an integer value based on the comparison:
- Returns 0 if both strings are equal.
- Returns a negative value if the calling string is lexicographically smaller than the compared string.
- Returns a positive value if the calling string is lexicographically greater than the compared string.
C++ Code: Comparing Boxes Using this Pointer
#include <iostream>
using namespace std;
class Box {
public:
// Constructor to initialize dimensions
Box(double l = 1.0, double b = 1.0, double h = 1.0) {
length = l;
breadth = b;
height = h;
}
// Function to calculate volume
double Volume() {
return length * breadth * height;
}
// Function to compare current box with another box
bool isBiggerThan(Box other) {
// Using this-> to refer to the current object's volume
return this->Volume() > other.Volume();
}
private:
double length, breadth, height;
};
int main() {
Box box1(3.0, 4.0, 5.0);
Box box2(2.0, 6.0, 4.0);
if (box1.isBiggerThan(box2)) {
cout << "Box1 is bigger." << endl;
} else {
cout << "Box2 is bigger or equal." << endl;
}
return 0;
}
What did we do?
We created two boxes and used a function isBiggerThan to compare them. Inside this function, this->Volume() means “calculate my own volume,” while other.Volume() refers to the other object. This is our machine knowing its own serial number!
Object Pointers — "Can we point at a box and ask for its volume?"
"What if we don’t want to use an object directly, but store its address instead?"
Imagine we’re at a warehouse and we don’t carry the whole box around. We just carry a tag that says “this box is in row 3, column 5.” That tag is a pointer!
In C++, we can do something similar with objects. Instead of calling functions on an object directly, we can use a pointer to that object.
C++ Code: Pointer to Object
#include <iostream>
using namespace std;
class Box {
public:
// Constructor
Box(double l = 1.0, double b = 1.0, double h = 1.0) {
length = l;
breadth = b;
height = h;
}
// Function to calculate volume
double Volume() {
return length * breadth * height;
}
private:
double length, breadth, height;
};
int main() {
Box box1(2.0, 3.0, 4.0);
Box* ptr = &box1; // Pointer to object
// Accessing function using pointer
cout << "Volume of box1 using pointer: " << ptr->Volume() << endl;
return 0;
}
What did we do?
We created a pointer ptr to the object box1. Instead of calling box1.Volume(), we used ptr->Volume(). The arrow (->) helps us access members using a pointer. It’s like saying, “Hey, I have this box’s address. Let me ask it for its volume.”
Static Data Members — "Can we share something between all boxes?"
"What if all boxes want to keep count of how many boxes were made?"
Now let’s think again. Suppose we work in the box factory and every time we make a new box, we increment a global counter. This counter doesn’t belong to a specific box, but to the factory as a whole.
That’s what static data members are. They don’t belong to individual objects — they’re shared across all of them.
#include <iostream>
using namespace std;
class Box {
public:
static int objectCount; // Shared among all objects
// Constructor
Box(double l = 1.0, double b = 1.0, double h = 1.0) {
length = l;
breadth = b;
height = h;
objectCount++; // Increment counter whenever a new box is made
}
double Volume() {
return length * breadth * height;
}
private:
double length, breadth, height;
};
// Initialization of static member outside the class
int Box::objectCount = 0;
int main() {
cout << "Initial count: " << Box::objectCount << endl;
Box b1(2.0, 3.0, 4.0);
Box b2(3.0, 3.0, 3.0);
cout << "Total boxes created: " << Box::objectCount << endl;
return 0;
}
What did we do?
We used static int objectCount to keep track of how many boxes were created. No matter how many objects we make, this variable stays in one place. It’s like putting a notebook at the entrance of the factory — every time a machine is installed, someone writes in the notebook. All machines share it.
Static Member Functions — "Can we check how many boxes exist without having a box?"
"How do we read the notebook if we haven’t made any boxes yet?"
Let’s say we want to know how many boxes were ever made. But we haven’t created a box yet. We still want to peek into that notebook at the factory entrance. We don’t need a box to read it — just need to be allowed to.
This is where static functions come in. These functions belong to the class, not to any specific object. And since they’re independent of objects, they don’t use this either.
#include <iostream>
using namespace std;
class Box {
public:
static int objectCount;
Box(double l = 1.0, double b = 1.0, double h = 1.0) {
length = l;
breadth = b;
height = h;
objectCount++;
}
static int getCount() {
// Can't use this pointer here — this is independent of objects
return objectCount;
}
private:
double length, breadth, height;
};
// Static member initialization
int Box::objectCount = 0;
int main() {
// Call static function before creating any object
cout << "Box count before making any box: " << Box::getCount() << endl;
Box b1(1.0, 2.0, 3.0);
Box b2(3.0, 2.0, 1.0);
// Call static function after object creation
cout << "Box count after making boxes: " << Box::getCount() << endl;
return 0;
}
What did we do?
We made a static function getCount() that can be called using the class name itself — no object needed! Since it doesn't belong to any one box, it doesn’t use this. It just opens the shared notebook and reads the count.
Recap: Let’s Tie It All Together
Let’s imagine we are the managers of a box factory:
- Every box knows its own size — and can compare itself with another (this pointer).
- Sometimes, instead of handling the boxes directly, we just carry their address (object pointers).
- The factory keeps a notebook that tracks how many boxes were made (static data members).
- We can peek into the notebook even before any box is created (static member functions).
Each of these concepts helps us build more organized, efficient, and realistic class structures in C++. And what’s really powerful is how they work together in real-world-like scenarios.