Thursday, August 26, 2010

Implementing One-to-Many Relationships in C++

The need to manage relations between objects is at the core of many business applications: objects refer to each other with various semantics, with or without inverse links, for various durations.

A very common case is the One-to-Many relation: a Owner object has relationships with several User objects. The owner must know which objects it is related to, and the users have an inverse link to their owner.  When a user is added to or removed from its owner, both links should be updated.  When the owner is destroyed, all the owned objects are also destroyed.  Such relations are extremely common, and often implemented with ad-hoc code.

This paper proposes a set of classes to implement those One-to-Many relations with a minimal amount of code to be written in the Owner and User classes, and performance equivalent to hand-written code, both in terms of memory and CPU.

Here is an example...

#include "relations.h"

#include <iostream>
#include <string>

using namespace relations;

class User;
class Owner;

class Named {
public:
  Named(const std::string& name) 
    : _name(name) {}
  const std::string& name() const {
    return _name;
  }
  ~Named() { 
    std::cout << "Object '" << _name << "' destroyed" << std::endl; 
  }
private:
  std::string _name;
};

class User 
  : public RelationUser<Owner, User>
  , public Named {
public:
  User(Owner* owner, const std::string& name)
    : RelationUser<Owner,User>(owner)
    , Named(name)
  {}
};

class Owner 
  : public RelationOwner<RelationUser<Owner, User> > {
public:
  void show();
};

void Owner::show() {
  std::cout << "Users of Owner: ";
  for (Owner::iterator it (begin()); it != end(); ++it) {
    std::cout << (*it)->name() << " ";
  }
  std::cout << std::endl;
}

void test()  {
  Owner* owner (new Owner());
  User* user1 (new User(owner, "1"));
  User* user2 (new User(owner, "2"));
  User* user3 (new User(owner, "3"));
  owner->show();

  delete user2;
  owner->show();

  delete owner; // All users deleted
}

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


And the output is:

  Users of Owner: 1 2 3
  User '2' destroyed
  Users of Owner: 1 3
  User '1' destroyed
  User '3' destroyed

Enjoy...