1

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
}
Community
  • 1
  • 1
lefunction
  • 301
  • 2
  • 16

2 Answers2

1

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.

Community
  • 1
  • 1
πάντα ῥεῖ
  • 1
  • 13
  • 116
  • 190
  • I have followed your instructions however none of my files will compile. I get the Error from Queue.h that "treePtr has not been declared" and "treePtr does not name a type" no matter which file I try to compile – lefunction Nov 11 '15 at 18:31
  • my original solution was to #include "Queue.cpp" in Tree.cpp, and declare the Queue object in Tree.cpp. This worked. – lefunction Nov 11 '15 at 18:34
  • @lefunction `treePtr` isn't visible from the forward declaration, hence the error. You could try forwarding the nested class/struct declaration as well, not sure if that's possible (would need an experiment). – πάντα ῥεῖ Nov 11 '15 at 18:37
  • @lefunction _"my original solution was to `#include` ..."_ Check the link I gave in my answer. Including translation units is the wrong approach. – πάντα ῥεῖ Nov 11 '15 at 18:39
  • Yes, I understand that. Hence why I am trying to declare the Queue as a private data member in Tree. I was just explaining the solution that did work. – lefunction Nov 11 '15 at 18:42
  • @lefunction The [experiment](http://ideone.com/F1TigF) failed, as I've expected. See my updates. – πάντα ῥεῖ Nov 11 '15 at 19:02
  • Thank you. I really appreciate you talking a look at this. There was a way I got it to work before. Maybe if I start from there I can figure out where all the data members should go, without including .cpp files. :) – lefunction Nov 11 '15 at 19:09
  • @lefunction Well, what you're trying to do regarding the nested types `tree` and `treePtr` is simply not possible. As I've mentioned the only way to make these `private` is hiding in an internally used namespace. – πάντα ῥεῖ Nov 11 '15 at 19:16
  • I am wondering if the problem is the fact that these classes are friends, and I am trying to declare as a private data member an object of type Queue in the Tree class. So my Tree is sharing an object Queue with the class Queue. Would that be a problem? – lefunction Nov 11 '15 at 19:26
  • 1
    @lefunction Hmm, `Queue` seems to be a good candidate for a template class. From your sample I actually can't see, why it needs to access any innards of the `tree` struct. You don't give a use case, why this tight coupling is actually needed. If you want to use a queue structure as container for your nodes, something like [`std::queue`](http://en.cppreference.com/w/cpp/container/queue) templated container should be sufficient. – πάντα ῥεῖ Nov 11 '15 at 19:39
  • Yes yes yes!!!! Making Queue a template class relieved the need to have access to the structures in my Tree class. Very edifying!!!!!! – lefunction Nov 11 '15 at 20:11
  • @lefunction Glad having been helpful to nudge you into the right direction ;-). – πάντα ῥεῖ Nov 11 '15 at 20:13
0

My best guess here is that the problem was that since the classes Queue and Tree were friends, and Tree had an instance of a Queue as a data member, there was some conflict when trying to include files and forward declare. By sharing data members with Queue, the Tree class shared an instantiation of a Queue object with the Queue class, so there was some inception sharing going on that was not obvious. @πάντα ῥεῖ suggested to make the Queue a template class so that it could accept objects of any type, without having to couple with the Tree (which was done so the Queue class would know what to do with treePtr objects). Making Queue a template class solved the problem because now the Tree class can have an instance of a Queue, and I can pass objects of type treePtr to the Queue without the Queue knowing anything about the Tree class before hand.

lefunction
  • 301
  • 2
  • 16