Search code examples
c++oopmethodsmembercircular-dependency

Resolving apparent circular dependency: class A having a method that takes in class B, while class B having a member of type class A


I have two classes that I want to define, Position and TangentVector, partially given as follows:

class Position
{
public:
    Position(double x, double y, double z);
    
    // getters
    double x(){ return m_x };
    double y(){ return m_x };
    double z(){ return m_x };

    void translate(const TangentVector& tangent_vector);
private:
    double m_x;
    double m_y;
    double m_z;
}
class TangentVector
{
public:
    Tangent(double x, double y, double z, Position position);

    // getters
    double x(){ return m_x };
    double y(){ return m_x };
    double z(){ return m_x };
private:
    double m_x;
    double m_y;
    double m_z;
    Position m_position;
}

The key thing to note with the classes is that TangentVector has a member of type Position (TangentVector depends on Position) while Position has a method that takes in an argument of type const TangentVector& (Position depends on TangentVector?).

For context's sake, Position is intended to represent a point on the unit sphere, and TangentVector describes a vector tangent to the sphere, with the origin of the vector specified by a Position. Since the definition of a VectorTangent requires a Position to be specified, it seems reasonable to say that VectorTangent depends on Position. However, now I want to define a function that takes a Position on the sphere, and "translates" it along the sphere by a direction and distance given by TangentVector. I would really like this translate method to live in the Position class, since it a function that modifies the state of Position. This would lend itself to the following usage, which I feel is fairly natural:

Position position{ 1.0, 0.0, 0.0 };
TangentVector tangent_vector{ 0.0, PI/2, 0.0, position };
position.translate(tangent_vector);                        // Now { 0.0, 1.0, 0.0 };

However, I fear that this results in some circular dependency. So...

  • Is this case an example of circular dependency? Is this case bad practice?
  • If so, how can this circular dependency be avoided? How can this code be modified such that it is in-line with good OOP practices?

(I considered making the Position m_position member a raw pointer instead. In this case, however, I intend m_position to be fully owned by TangentVector, rather than allow the possibility of it being altered external to the class. In the example usage code, I do not want the translate method to modify tangent_vector, which would happen if tangent_vector's constructor took in position as a pointer and stored it as a member.)


Solution

  • class Position takes only a reference to class TangentVector. Therefore you might pre-declare TangentVector as class TangentVector; before the declaration of class Position:

    class TangentVector;
    
    class Position
    {
    public:
        Position(double x, double y, double z);
        
        // getters
        double x(){ return m_x };
        double y(){ return m_x };
        double z(){ return m_x };
    
        void translate(const TangentVector& tangent_vector);
    private:
        double m_x;
        double m_y;
        double m_z;
    };