Codementor Events

Smart Pointers in C++ - Part 5

Published Jul 01, 2021Last updated Aug 18, 2024
Smart Pointers in C++ - Part 5

Introduction

Welcome to Part #5 of Smart Pointers in C++!

The Parts #1, #2, #3 and #4 are a prerquisite for Part #5 and can be found here

We have discussed unique_ptr and shared_ptr in detail. Let's focus on weak_ptr in this part.

A weak_ptr is a bit similar to a shared_ptr, in that, we can have multiple weak_ptrs owning a common object. It differs from shared_ptr by not taking part in the reference count. In other words, a weak_ptr does not increase reference count of the owned object. A weak_ptr must be used in conjunction with a shared_ptr. It's used when we want to observe a resource, but do not want to control the life cycle of the resource.

A pitfall of shared_ptr

One problem with using shared_ptr is that of Circular references.

Circular reference is a series of references such that each object i references the object i+1 and the last object references the first, thus forming a reference loop. To find out what's wrong with a circular reference, let's consider an example.

// forward declaration
struct Bar;

struct Foo {
  std::shared_ptr<Bar> pBar;
};

struct Bar {
  std::shared_ptr<Foo> pFoo;
};

We have two structs referencing each other. This is an example of circular reference.

void someFunction() {
  // create struct objects
  std::shared_ptr<Foo> f = std::make_shared<Foo>();
  std::shared_ptr<Bar> b = std::make_shared<Bar>();

  // circular reference
  f->pBar = b;
  b->pFoo = f;

  // do something else
}

When the function returns, the two objects created (one each of Foo and Bar) go out of scope. According to working of smart pointers, both objects should get automatically destroyed. But, this does not happen.

When destructor for Bar runs, it checks whether there are any shared references to this object. Currently, there is one — reference pBar that belongs to Foo (b has already gone out of scope). So the Bar object initially co-owned by b will not be destructed. When destructor for Foo runs, the same happens. It checks for shared references and finds that pFoo that belongs to Bar owns the object. So the Foo object initially co-owned by f will not be destructed.

When the program ends, there are two objects that never got destroyed. We have a memory leak!

Solution

Make use of weak_ptr to break the circular reference. So we change the shared_ptr in the previous example to weak_ptr.

// forward declaration
struct Bar;

struct Foo {
  std::weak_ptr<Bar> pBar;
};

struct Bar {
  std::weak_ptr<Foo> pFoo;
};

The code for someFunction() stays the same.

void someFunction() {
  // create struct objects
  std::shared_ptr<Foo> f = std::make_shared<Foo>();
  std::shared_ptr<Bar> b = std::make_shared<Bar>();

  // no circular reference here
  f->pBar = b;
  b->pFoo = f;
  // the copy assignment of weak_ptr is getting called here.
  // It automatically *casts* shared_ptr to weak_ptr.

  // do something else
}

Now, when the someFunction() function runs, we will not have a memory leak. Why? Because, when destroying the two objects that were created, there were no shared_ptr references to those objects. There were weak_ptr references; and weak_ptr references do not keep a resource alive. So both the objects get destroyed when their destructor runs.

Converting weak_ptr to shared_ptr

Using a weak_ptr we cannot access the owned object. There are no * and -> operators defined for weak_ptr. We have to get a shared_ptr to the owned object before using it. The lock() function of weak_ptr helps us do exactly that. It returns a shared_ptr object with the information preserved bby the weak_ptr. If the object has already been destroyed, lock() returns a shared_ptr object with default values.

void weakPtrDemo() {
  std::shared_ptr<int> sp1, sp2;
  std::weak_ptr<int> wp;

  sp1 = std::make_shared<int> (20);
  wp = sp1; // wp also owns the int

  // try getting shared_ptr from wp
  sp2 = wp.lock();
  sp1.reset(); // sp1 releases the ownership, but sp2 still owns the int

  sp1 = wp.lock(); // get the ownership back, now sp1 and sp2 co-own the int

  std::cout << "*sp1: " << *sp1 << std::endl; // 20
  std::cout << "*sp2: " << *sp2 << std::endl; // 20

  sp1.reset();
  sp2.reset();
  // the int will be freed as no shared pointer owns it.

  // lock() returns shared_ptr with default value
  // (nullptr in this case)
  sp1 = wp.lock();
  
  std::cout << "is sp1 valid: " << std::boolalpha
            << static_cast<bool> (sp1) << std::endl; // false
}

Conclusion

Now that we know how to use smart pointers, let's try and avoid raw pointers and start making use of smart pointers.

Discover and read more posts from Sandesh Patil
get started
post comments5Replies
George Mcbrine
7 months ago

Your post is informative for C++ learners, Get expert CV assistance for your CV with our online CV writing services company. A team of local Irish CV writers is ready to help. They will efficiently display your skills, experience, and career goals. A professionally crafted CV always leaves a lasting impression on recruiters. Don’t waste your time and hire us today.
https://www.cvwritingservices.ie

Australian assignment Help
7 months ago

Your article on smart pointers in C++ is not just informative but also serves as a practical guide for developers grappling with memory management in their code and it is positively used by the Student Life Saviour experts at: https://studentlifesaviour.com
Part #5 delves into the world of weak_ptr, shedding light on its utility and the pitfalls of circular references often encountered with shared_ptr.

Ayden Watkins
8 months ago

This article provides a comprehensive overview of smart pointers in C++, focusing on weak_ptr. It effectively explains the distinction between weak_ptr and shared_ptr, emphasizing that weak_ptr doesn’t contribute to reference counting and is used for observing resources without managing their lifecycle of allessaywriter. However, it could benefit from examples illustrating real-world scenarios where weak_ptr is particularly useful, enhancing readers’ understanding. Overall, it’s a valuable resource for understanding smart pointer usage in C++.

Visit us:- https://allessaywriter.com/

Show more replies