Weak Pointers and Circular References in C++ 11: Listing 3

Weak_ptr is an exhaust vent for circular references.

// person.h – Correct version (see Figure 2)
#ifndef PERSON_H_
#define PERSON_H_

#include <memory>
#include <string>

using namespace std;

class person {
  friend void get_couple_married(shared_ptr<person>, shared_ptr<person>);
public:
  person() = delete;
  person(const string);
  ~person();

  string get_name() const;
  shared_ptr<person> get_spouse() const;
private:
  // nested class that models a smart pointer that could be strong or weak
  class spouse_ptr {
  private:
    shared_ptr<person> strong_;
    weak_ptr<person> weak_;
    spouse_ptr& set(const shared_ptr<person>);
  public:
    spouse_ptr();
    spouse_ptr(const shared_ptr<person>);

    spouse_ptr& operator=(const shared_ptr<person>);

    shared_ptr<person> get() const;
  };
  string name_;
  spouse_ptr spouse_;

  void set_spouse(const shared_ptr<person>);
};

#endif /* PERSON_H_ */
/* ---------------------------------- */
// person.cpp
#include "person.h"
#include <iostream>
#include <stdexcept>

using namespace std;

person::person(const string name) : name_{name} {
  cout << name_ << " instance created." << endl;
}

person::~person() {
  cout << name_ << " instance to be disposed." << endl;
}

string person::get_name() const { return name_; }

shared_ptr<person> person::get_spouse() const { return this->spouse_.get(); } 

void person::set_spouse(const shared_ptr<person> sp) {
  this->spouse_ = sp;
}

person::spouse_ptr::spouse_ptr() : strong_{nullptr}, weak_{} {}
person::spouse_ptr::spouse_ptr(const shared_ptr<person> sp) : strong_{nullptr}, weak_{} {
  set(sp);
}

person::spouse_ptr& person::spouse_ptr::operator=(const shared_ptr<person> sp) {
  return set(sp);
}

shared_ptr<person> person::spouse_ptr::get() const {
  if (strong_!=nullptr)
    return strong_;
  else
    return weak_.lock();
}

person::spouse_ptr& person::spouse_ptr::set(const shared_ptr<person> sp) {
  // the strong or weak reference is used depending on 
  if (sp->get_spouse()==nullptr) {
    strong_ = sp;
    weak_.reset();
  } else {
    strong_ = nullptr;
    weak_ = sp;
  }

  return *this;
}

void get_couple_married(shared_ptr<person> husband, shared_ptr<person> wife) {
  // checks arguments aren’t null
  if ((husband==nullptr)||(wife==nullptr))
    throw invalid_argument("get_couple_married(husband, wife): can't get nullptr as couple member");

  // checks arguments aren’t the same instance
  if (husband.get()==wife.get())
    throw invalid_argument("get_couple_married(husband, wife): husband and wife can't be the same person");

  // the husband strongly points to his wife, while she weakly points back to him
  // (see function person::spouse_ptr::set)
  husband->set_spouse(wife);
  wife->set_spouse(husband);
}
/* ---------------------------------- */
// main.cpp
#include <iostream>
#include "person.h"

using namespace std;

void get_couple_married() {
  shared_ptr<person> husband = make_shared<person>("John"), wife = make_shared<person>("Pocahontas");
  get_couple_married(husband, wife);
  cout << husband->get_name() << "'s wife is " << husband->get_spouse()->get_name() << "." << endl;
  cout << wife->get_name() << "'s husband is " << wife->get_spouse()->get_name() << "." << endl;
}

int main() {
  get_couple_married();
  return 0;
}

About the Author

Diego Dagum is a software architect and developer with more than 20 years of experience. He can be reached at email@diegodagum.com.