Rule A20-8-7 (required, implementation, non-automated)

A std::weak_ptr shall be used to represent temporary shared ownership.

Rationale

A cyclic structure of std::shared_ptr results in reference counting mechanism never dropping to zero, which prevents from pointed object deallocation. Breaking such

cycles is done using std::weak_ptr which must be converted to std::shared_ptr in order to access the referenced object.

Example

// $Id: A20-8-7.cpp 308795 2018-02-23 09:27:03Z michal.szczepankiewicz $

#include <memory>

template <template <typename> class T, typename U>
struct Base
{
T<U> sp;
};

template <typename T>
using Shared = Base<std::shared_ptr, T>;

template <typename T>
using Weak = Base<std::weak_ptr, T>;

struct SBarSFoo;
struct SFooSBar : public Shared<SBarSFoo> {};
struct SBarSFoo : public Shared<SFooSBar> {};

struct A : public Shared<A> {};

struct WBarSFoo;
struct SFooWBar : public Shared<WBarSFoo> {};
struct WBarSFoo : public Weak<SFooWBar> {};

int main()
{
std::shared_ptr<SFooSBar> f = std::make_shared<SFooSBar>();
std::shared_ptr<SBarSFoo> b = std::make_shared<SBarSFoo>();
f->sp = b;
b->sp = f;
//non-compliant, both f and b have ref_count() == 2
//destructors of f and b reduce ref_count() to 1,
//destructors of underlying objects are never called,
//so destructors of shared_ptrs sp are not called
//and memory is leaked

std::shared_ptr<A> a = std::make_shared<A>();
a->sp = a;
//non-compliant, object ’a’ destructor does not call
//underlying memory destructor

std::shared_ptr<SFooWBar> f2 = std::make_shared<SFooWBar>();
std::shared_ptr<WBarSFoo> b2 = std::make_shared<WBarSFoo>();
f2->sp = b2;
b2->sp = f2;

//compliant, b2->sp holds weak_ptr to f2, so f2 destructor
//is able to properly destroy underlying object

return 0;
}

See also

C++ Core Guidelines [11]: R.24: Use std::weak_ptr to break cycles of shared_ptrs