Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to solve a circular class dependency with shared base class without headers?

So I am creating a project that involves having vectors nested inside one another, but the structure won't compile. I think this is a case of circular dependency, but all of the fixes I've seen seem to only apply to code involving header files and separate translation units.

#include <iostream>
#include <vector>

class A {
public:
    virtual ~A();
};

// The following forward declaration statements didn't solve the
// compiler error:
// class C;
// class C : public A;

class B : public A {
    std::vector<A*> Avector;    
public:
    void addC(C* Cin){Avector.push_back(Cin);}
    ~B();
};

class C : public A {
    std::vector<A*> Avector;        
public:
    void addB(B* Bin){Avector.push_back(Bin);}
    ~C();
};

int main() {
    B b;
    C c;
    b.addC(&c);
    c.addB(&b);
}

I've tried forward declaring with

class C;
Class C : public A;

But none of it seems to work.

The error I get is:

\test\test\source.cpp(15): error C2061: syntax error : identifier 'C'
\test\test\source.cpp(15): error C2065: 'Cin' : undeclared identifier

Is this sort of code implementable?

like image 748
Sr1703 Avatar asked May 05 '15 16:05

Sr1703


2 Answers

"... but all of the fixes I've seen seem to only apply to code involving header files and separate translation units."

I well agree that a popular Q&A like Resolve circular dependencies in c++ doesn't really answer your question how to handle this within a single implementation file (header or not).

However IMHO it's considered the better practice (and as you could pick up by these pointers), to separate out class declarations in headers and implementation/definition into their own translation units.
Even with header only (e.g. template class) libraries, you should consider to provide a separate class declaration (without inlined code), and provide the implementation in a separate source file, that's included there.

Your problem is actually here:

class B : public A {
    // ...
    void addC(C* Cin) { Avector.push_back(Cin); }
                   // ^^^^^^^^^^^^^^^^^^^^^^^^^^^
    // ...
};

At this point (even with a forward declaration of class C;) the compiler doesn't know how to convert C* to A*, because the information about the inheritance relationship is missing (and no, you can't make forward declarations like class C : public A;).

"Is this sort of code implementable?"

You can do all that in a single translation unit/header file:

Instead of inlining the function definition with B's class declaration, you should separate it out to be seen after the full declaration of class C:

class C;

class B : public A {
    // ...
    void addC(C* Cin);
};

class C : public A {
    // ...
};

inline void B::addC(C* Cin) { Avector.push_back(Cin); }

class C will see the full declaration of class B anyway (because of declaration order). Thus it's OK to use the implementation

void addB(B* Bin) { Avector.push_back(Bin); }

from within the class C declaration.

like image 167
πάντα ῥεῖ Avatar answered Sep 19 '22 04:09

πάντα ῥεῖ


I'd tried forward declaring, and I'd tried putting the function implementation after the second part, but I hadn't tried both at once. The fixed code for anyone googling this 10 years later:

  #include<iostream>
    #include<vector>

    using namespace std;

    class C;

    class A
    {
    public:
        virtual ~A();
    };

    class B : public A
    {
    public:
        vector<A*> Avector;

        void addC(C* Cin);
        ~B();
    };

    class C : public A
    {
    public:
        vector<A*> Avector;

        void addB(B* Bin)
        {
            Avector.push_back(Bin);

        };
            ~C();
        };

    void B::addC(C* Cin)
    {
        Avector.push_back(Cin);
    }

    int main()
    {

    }

My thanks to πάντα ῥεῖ , and a shout out to Jarod42.

like image 35
Sr1703 Avatar answered Sep 19 '22 04:09

Sr1703