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

People and their best friends: a non-deterministic circular reference example.

// person.h
#ifndef PERSON_H_
#define PERSON_H_

#include <string>
#include <memory>
#include "circular_ptr.h"

using namespace std;

class person {
  friend circular_ptr<person> *get_circular_ptr(const shared_ptr<person>&);
public:
  person()=delete;
  person(const string);
  ~person();

  string get_name() const;
  void set_best_friend(shared_ptr<person>);
  shared_ptr<person> get_best_friend() const;
  void set_no_best_friend();
private:
  class best_friend_ptr : public circular_ptr<person> {
  public:
    best_friend_ptr();
    best_friend_ptr(shared_ptr<person>&);
    best_friend_ptr& operator=(shared_ptr<person>&);
  };

  string name_;
  best_friend_ptr best_friend_;
};

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

using namespace std;

person::person(const string name) : name_ {name}, best_friend_ {} {
  if (name_=="") throw invalid_argument("A person must have a non-empty name");
  cout << name_ << " instance created." << endl;
}

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

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

void person::set_best_friend(shared_ptr<person> best_friend) {
  if (this==best_friend.get())
    throw invalid_argument("Best friend can't be self person");
  best_friend_ = best_friend;
}

shared_ptr<person> person::get_best_friend() const {
  return best_friend_.get();
}

void person::set_no_best_friend() {
  best_friend_.reset();
}

person::best_friend_ptr::best_friend_ptr() : circular_ptr::circular_ptr() {}
person::best_friend_ptr::best_friend_ptr(shared_ptr<person> &p) : circular_ptr::circular_ptr(p) {}

person::best_friend_ptr& person::best_friend_ptr::operator=(shared_ptr<person> &t) {
  return static_cast<person::best_friend_ptr&>(circular_ptr::operator=(t));
}

circular_ptr<person> *get_circular_ptr(const shared_ptr<person> &p) {
  return &(p->best_friend_);
}
/* ---------------------------------- */
// main.cpp
#include <iostream>
#include <string>
#include <memory>
#include <vector>
#include "person.h"

using namespace std;

void print_friendship(vector<shared_ptr<person>> vp) {
for (auto p : vp) {
      auto q = p->get_best_friend();
      cout << p->get_name() << "'s best friend is " << (q ? q->get_name() : "nobody") << endl;
    }
}

void make_friends() {
  shared_ptr<person> john = make_shared<person>("John"),
                     charles = make_shared<person>("Charles"),
                     emma = make_shared<person>("Emma"),
                     cindy = make_shared<person>("Cindy"),
                     arthur = make_shared<person>("Arthur"),
                     laurie = make_shared<person>("Laurie");

  vector<shared_ptr<person>> vp = {john, charles, emma, cindy, arthur, laurie};

  john->set_best_friend(charles);  // strong
  charles->set_best_friend(emma);  // strong
  emma->set_best_friend(cindy);    // strong
  cindy->set_best_friend(arthur);  // strong
  arthur->set_best_friend(laurie); // strong
  laurie->set_best_friend(john);   // weak! See Figure 3
  print_friendship(vp);

  cindy->set_best_friend(charles); // weak, but also laurie’s ptr to john
                                   // becomes strong (see Figure 4)
  print_friendship(vp);

  john->set_best_friend(cindy);    // strong
  emma->set_best_friend(arthur);   // weak, but also cindy’s ptr to charles
                                   // becomes strong (see Figure 5)
  print_friendship(vp);

  charles->set_no_best_friend();   // emma’s ptr to arthur becomes strong
  print_friendship(vp);            // See Figure 6
}

int main() {
  make_friends();

  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.