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

Restricting circular references to a conceptual level.

#ifndef PERSON_H_
#define PERSON_H_

#include <string>
#include <memory>
#include <map>

using namespace std;

class person;

// this class models a table of friendship (name, best friend)
class friendship {
public:
  void set_best_friend(shared_ptr<person>, const shared_ptr<person>);
  shared_ptr<person> get_best_friend(const shared_ptr<person>) const;
  void set_no_best_friend(shared_ptr<person>);
private:
  // hashtable
  map<string, shared_ptr<person>> container_;
};

class person {
public:
  person()=delete;
  person(const string);
  ~person();

  string get_name() const;
private:
  string name_;
};

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

using namespace std;

void friendship::set_best_friend(shared_ptr<person> p, const shared_ptr<person> best_friend) {
  if (p.get()==best_friend.get())
    throw invalid_argument("Best friend can't be self person");

  auto name = p->get_name();
  container_.erase(name);
  if (best_friend)
    container_[name] = best_friend;
}

shared_ptr<person> friendship::get_best_friend(const shared_ptr<person> p) const {
  auto i = container_.find(p->get_name());
  if (i==end(container_))
    return nullptr;
  else
    return i->second;
}

void friendship::set_no_best_friend(shared_ptr<person> p) {
  set_best_friend(p, nullptr);
}

person::person(const string name) : name_ {name} {
  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_;
}
/* ---------------------------------- */
// main.cpp
#include <iostream>
#include <string>
#include <memory>
#include <vector>
#include "person.h"

using namespace std;

void print_friendship(const vector<shared_ptr<person>> &vp, const friendship& f) {
for (auto p : vp) {
      auto q = f.get_best_friend(p);
      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");

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

  // now friendship is kept outside person instances, to eliminate all chance of circular
  // references
  f.set_best_friend(john, charles);
  f.set_best_friend(charles, emma);
  f.set_best_friend(emma, cindy);
  f.set_best_friend(cindy, arthur);
  f.set_best_friend(arthur, laurie);
  f.set_best_friend(laurie, john);
  print_friendship(vp, f);

  f.set_best_friend(cindy, charles);
  print_friendship(vp, f);

  f.set_best_friend(john, cindy);
  f.set_best_friend(emma, arthur);
  print_friendship(vp, f);

  f.set_no_best_friend(charles);
  print_friendship(vp, f);
}

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.