Search code examples
c++includeforward-declaration

Friend classes need to include or forward declare c++?


I have been struggling with errors when trying to build a binary tree using a queue. The problem is what classes should include what files and how to reference objects from the other classes? I threw my files into an IDE in an attempt to pinpoint just what the problems are and the results are below. Currently my issue is that in the Queue.h file, treePtr "does not name a type". You can see the evolution of this problem here This question is different from other posts because the two classes are friend classes. This brings up the problem of circular dependencies. I have tried all sorts of combinations of including files and forward declaring but one combination causes one type of issue, and another creates different errors.

Here is the main class:

#include <cstdlib>
#include "Tree.cpp"

using namespace std;

int main() {

    Tree tree;
    tree.addTreeNode(5);

return 0;

}

Here is the Queue.h:

#ifndef QUEUE_H_
#define QUEUE_H_

class Tree;  //Was instructed to put this here

class Queue {

    friend class Tree;

    private:
        typedef struct node {
            Tree::treePtr treeNode; //Here is the problem
            node* next;
        }* nodePtr;

        nodePtr head;
        nodePtr current;

public:
    Queue();
    virtual ~Queue();
    void push(Tree::treePtr t);  //Here is the problem
    int pop();
    void print();

};

#endif /* QUEUE_H_ */

This is Tree.h:

#ifndef TREE_H_
#define TREE_H_

#include "Queue.h"  //Was instructed to put this here

class Tree {
    friend class Queue;

    private:

        Queue q;  //Edit: Most likely problem since Queue and Tree are friends

        typedef struct tree {
            int data;
            tree* left;
            tree* right;
        }* treePtr;

        treePtr root;
        int numNodes;

public:
    Tree();
    virtual ~Tree();
    void addTreeNode(int integer);
};

#endif /* TREE_H_ */

This is tree.cpp

#include <cstdlib>
#include <iostream>

#include "Tree.h"

using namespace std;

Tree::Tree() {
    root = NULL;
    numNodes = 0;
}

void Tree::addTreeNode(int integer) {
    numNodes++;
    treePtr t = new tree;
    t->left = NULL;
    t->right = NULL;
    t->data = integer;

    cout << "add root\n";
    root = t;
    q.push(t);  //This is a problem
    q.print();

}

Tree::~Tree() {
    // TODO Auto-generated destructor stub
}

Solution

  • You have to compile Tree.cpp and (I suppose you have one) Queue.cpp separately, instead of including Tree.cpp in your main.cpp.

    Forward declarations are fine for friending classes, even if you do so circular.

    Put #include "Tree.h" in your Queue.cpp file, to let the compiler see the full declaration.

    In main.cpp just put #include " Tree.h".

    To get the final executable link all of the produced object files main.o(bj), Tree.o(bj) and Queue.o(bj).

    See also [Why should I not include cpp files and instead use a header?] please.


    As I've noticed now your actual problem is, you cannot access nested classes/structs from a forward declared class/struct as you're requiring with accessing treePtr from Queue (treePtr should be better named something like TreeNode or similar BTW).

    You cannot make treePtr a private nested type in this case, it must be publicly visible.

    A viable way is to put treePtr in a namespace internal_, that indicates it's not intended for usage outside the API.


    Another viable way is to make Queue a template class, that accepts any type of tree or other kind of nodes. Since can't see any use case, why Queue needs to know about the internal specifications of tree (besides the trivial stuff like copying aso.), it's not really necessary to make Queue a friend class.