JnBrymn
JnBrymn

Reputation: 25373

Circular Dependency in C++

The facts:

To me it seems that this is a decent pattern, but since a Manager has a list of Specialists and a Specialist has a Manager I'm getting circular dependency problems.

Is this a case where I should somehow forward declare the existence of one class from another? (If so, how?) Or should I use some design pattern to fix this problem? (If so what?) Also... I though the pattern itself was pretty o.k. so I wouldn't mind someone helping me understand why this is a bad thing.

Upvotes: 19

Views: 10202

Answers (5)

Flinsch
Flinsch

Reputation: 4340

It's a matter of taste, but forward declaration often is a good alternative to includes in header files even without circular dependencies. (I don't want to raise a discussion on that in this place.) So, here is an example on how to apply forward declarations for your problem:

In Manager.h:

// Forward declaration:
class Specialist;

// Class declaration:
class Manager
{
    // Manager declarations go here.
    // Only pointers or references to
    // the Specialist class are used.
};

In Manager.cpp:

#include "Manager.h"

#include "Specialist.h"

// Manager definitions/implementations
// using the Specialist class go here.
// Full Specialist functionality can be used.

In Specialist.h:

// Forward declaration:
class Manager;

// Class declaration:
class Specialist
{
    // Specialist declarations go here.
    // Only pointers or references to
    // the Manager class are used.
};

In Specialist.cpp:

#include "Specialist.h"

#include "Manager.h"

// Specialist definitions/implementations
// using the Manager class go here.
// Full Manager functionality can be used.

Upvotes: 13

jmucchiello
jmucchiello

Reputation: 18984

While everyone else is answering the core question I thought I'd point this out.

At runtime, the Manager creates and stores a list of Specialists. Then the Manager iterates through the list and asks each Specialist to initialize. During their initialization, each Specialist asks the Manager to supply them with other Specialists that fulfill some description. Once this is complete, the Manager then goes into a loop during which the Specialists are asked sequentially to perform their specialized task.

I just want to point out that this needs to be a two-step process. How can the manager tell specialist 1 what specialists exist for task B if the manager only knows about one specialist so far? So you need:

1) manager goes through list of specialists and asks them to identify themselves.

2) manager goes through list of specialists and asks them what specialties they need access to, telling them who can fulfill their requirements.

3) manager goes though list of specialists and tells them to perform their actions.

Upvotes: 1

Peter Alexander
Peter Alexander

Reputation: 54270

In both cases, forward declare the other class:

Manager.h

class Specialist;

class Manager
{
    std::list<Specialist*> m_specialists;
};

Specialist.h

class Manager;

class Specialist
{
    Manager* m_myManager;
};

The only time you need to bring in the header file for a class is when you need to use a member function or variable within that class, or need to use the class as a value type etc. When you only need a pointer or reference to a class, a forward declaration will suffice.

Note that forward declarations aren't just for solving circular dependencies. You should use forward declarations wherever possible. They are always preferable to including an extra header file if it is at all viable.

Upvotes: 25

pm100
pm100

Reputation: 50190

this is normal stuff. You just need

class Manager;

in the specialist header and

class Specialist; 

in the manager header

if you are using shared_ptrs you might find shared_from_this useful. (Not for looping but because it sounds like you will need it anyway)

Upvotes: 1

James McNellis
James McNellis

Reputation: 355099

One option is to forward declare one of the people, as you suggest:

struct specialist;

struct manager
{
    std::vector<std::shared_ptr<specialist> > subordinates_;
};

struct specialist
{
    std::weak_ptr<manager> boss_;
};

However, if you end up having more of a tree-structure (where you have multiple layers of management, a person base class would also work:

struct person
{
    virtual ~person() { }
    std::weak_ptr<person> boss_;
    std::vector<std::shared_ptr<person> > subordinates_;
};

You can then derive specific classes for different types of people in the hierarchy. Whether or not you need this depends on how exactly you intend to use the classes.

If your implementation doesn't support std::shared_ptr, it may support std::tr1::shared_ptr or you can use boost::shared_ptr.

Upvotes: 1

Related Questions