Introduction: The eternal beginner's question
\n\nIf you've ever browsed Stack Overflow in the C++ section, you've likely seen a question with thousands of views: "Why should I use a pointer instead of the object itself?". And this isn't idle curiosity. Everyone who starts learning C++ after Python or JavaScript faces this dilemma. On the surface, an object seems convenient: create it, call a method, forget about it. Why then all these asterisks, arrows, and new?
In reality, the answer lies in how C++ manages memory under the hood. In this article, we won't just give a dry definition; we'll break down real-world scenarios from a programmer's life where a pointer becomes not just convenient, but the only correct choice. Ready to peek into the engine of your code? Let's go!
\n\nObject vs Pointer: What does the compiler say?
\n\nLet's get straight to the point. Imagine you're writing a Monster class for your RPG game.
class Monster {\npublic:\n Monster() { std::cout << "Monster created!\\n"; }\n ~Monster() { std::cout << "Monster destroyed!\\n"; }\n void roar() { std::cout << "RAAAWR!\\n"; }\n};\n\nNow, two ways to use this class:
\n\n1. Object on the stack (automatic management)
\nvoid spawnOnStack() {\n Monster m; // Object lives here\n m.roar();\n} // Here m is automatically destroyed\n\n2. Pointer to the heap (manual management)
\nvoid spawnOnHeap() {\n Monster* m = new Monster(); // Object on the heap\n m->roar();\n delete m; // Must delete it ourselves!\n}\n\nThe first option seems simpler and safer. So why the second? Under the hood, there's a key difference: stack and heap.
\n\n- \n
- Stack — small, fast, but the object lives only inside the
{}block. \n - Heap — large, slower, but the object lives until you delete it. \n
When the object size is huge (e.g., a 4K texture), storing it on the stack risks a stack overflow. That's where pointers come into play.
\n\nTop 5 reasons to use a pointer (instead of an object)
\n\nNow that we understand the basic difference, let's break down specific situations where a pointer is not a whim, but a necessity.
\n\n1. Polymorphism: when the type isn't known upfront
\nThis is perhaps the most common reason. Suppose you have a base class Character and derived classes Warrior, Mage. If you create an object on the stack, you must specify the exact type:
Warrior w; // Fine, but not polymorphic\n\nBut what if the character type is chosen during gameplay? Only a pointer provides flexibility:
\n\nCharacter* hero;\nif (choice == "warrior")\n hero = new Warrior();\nelse\n hero = new Mage();\n\nhero->attack(); // Calls the derived class method!\ndelete hero;\n\nUnder the hood: the compiler uses a virtual function table (vtable). The pointer stores the address where the object resides, and when calling a method, it looks up the vtable. This trick won't work with a stack object — the type is rigidly fixed.
\n\n2. Passing to a function without copying
\nLook at this code:
\n\nvoid process(Monster m) { // Pass by value!\n m.roar();\n}\n\nMonster bigBoss;\nprocess(bigBoss); // Copying!\n\nHere, process receives a copy of the object. If Monster contains a 100 MB array, you've just copied 100 MB for nothing. A pointer solves the problem:
void process(Monster* m)