Article by Ayman Alheraki on January 11 2026 10:32 AM
Object-Oriented Programming (OOP) is one of the most popular and widely used programming paradigms in modern software development. C++ is a powerful language that provides advanced support for OOP, allowing developers to build complex, well-designed applications.
With the evolution of C++ standards, many new tools have been introduced to improve memory management, among which are Smart Pointers introduced in C++11. Smart pointers help prevent memory leaks and simplify dynamic memory management, making them ideal tools when using OOP in C++.
In this article, we will explore best practices for using OOP in modern C++ with smart pointers.
Before diving into examples, it's important to understand the different types of smart pointers in C++ and their uses:
std::unique_ptr: Used to manage sole ownership of an object. unique_ptr cannot be copied, ensuring that the object has only one owner.
std::shared_ptr: Used to manage shared ownership of an object. Multiple smart pointers of type shared_ptr can point to the same object. It uses a reference count to determine when to release memory.
std::weak_ptr: Used in conjunction with shared_ptr to avoid reference cycles. It does not affect the reference count, helping to avoid memory leaks in cases where shared pointers reference each other.
When using OOP with smart pointers, several patterns and practices can be followed to ensure efficient, clean, and maintainable code.
std::unique_ptrLet's take a simple example of managing an object of class Person using std::unique_ptr:
xxxxxxxxxx
// Definition of the Person classclass Person {public: Person(const std::string& name) : name(name) { std::cout << "Constructor: " << name << " created.\n"; }
~Person() { std::cout << "Destructor: " << name << " destroyed.\n"; }
void greet() const { std::cout << "Hello, my name is " << name << ".\n"; }
private: std::string name;};
int main() { // Create an object using std::unique_ptr std::unique_ptr<Person> person = std::make_unique<Person>("John");
person->greet(); // Call a class method
// No need to manually delete the object, it will be automatically destroyed when it goes out of scope return 0;}In the example above, std::unique_ptr is used to create an object of class Person. When the scope of person ends, the destructor is automatically called without needing manual memory management.
std::shared_ptrSometimes, you might need to share an object between multiple objects. In this case, std::shared_ptr can be used:
class Resource {public: Resource() { std::cout << "Resource acquired\n"; }
~Resource() { std::cout << "Resource destroyed\n"; } void use() { std::cout << "Resource is being used\n"; }};
void processResource(std::shared_ptr<Resource> res) { res->use();}
int main() { std::shared_ptr<Resource> res1 = std::make_shared<Resource>();
{ std::shared_ptr<Resource> res2 = res1; // res2 shares ownership with res1 processResource(res2); } // res2 goes out of scope but res1 still retains ownership
res1->use(); // The object can still be used
return 0;}In this example, the Resource object is shared between res1 and res2. The memory is only released when both res1 and res2 go out of scope.
std::weak_ptrWhen using std::shared_ptr, there is a possibility of creating a reference cycle (where objects point to each other using shared_ptr), which can lead to memory not being released. To avoid this, std::weak_ptr can be used as shown in the following example:
class B; // Forward declaration of class B
class A {public: std::shared_ptr<B> b_ptr; ~A() { std::cout << "A destroyed\n"; }};
class B {public: std::weak_ptr<A> a_ptr; // Use weak_ptr to avoid reference cycle ~B() { std::cout << "B destroyed\n"; }};
int main() { auto a = std::make_shared<A>(); auto b = std::make_shared<B>();
a->b_ptr = b; b->a_ptr = a; // Using weak_ptr
return 0;}In this example, std::weak_ptr is used in class B to avoid a reference cycle between A and B. This ensures that memory is properly released when the scope ends.
Use std::unique_ptr when you need unique ownership and want to ensure no sharing of the pointer.
Use std::shared_ptr when you need shared ownership, but beware of reference cycles.
Use std::weak_ptr to avoid reference cycles when using std::shared_ptr.
Smart pointers are a powerful tool in modern C++ that help write safer and more efficient code. By using smart pointers with object-oriented programming, you can build more flexible and maintainable programs while reducing errors associated with memory management.
Using these tools correctly requires a deep understanding of how they work and the situations in which they are most appropriate. By following best practices and learning how to use each type of smart pointer, you can improve the quality and efficiency of the software you develop using C++.