-5

Why does following C++ code gives below mentioned error? Also why is not this the idiomatic way of writing recursive data-structures in C++? Is there something fundamentally wrong with this way of writing C++?

#include<iostream>
using namespace std;


class tree{
public:
    virtual void inorder() {};
};

class emp_tree: public tree{
public:
    void inorder(){
    }
};

class non_emp_tree: public tree{
public:
    tree left, right;
    int val;
    non_emp_tree(int v, tree l, tree r): val(v), left(l), right(r) {};
    void inorder(){
        left.inorder();
        cout<<" "<<val<<" ";
        right.inorder();
    }
};




int main() {
    tree leaf1 = non_emp_tree(1, emp_tree(), emp_tree());
    tree leaf2 = non_emp_tree(3, emp_tree(), emp_tree());
    tree root = non_emp_tree(2, leaf1, leaf2);
    root.inorder();
    return 0;
}

Error given by compiler: (I'm unable to comprehend most of it)

/tmp/ccAjhirw.o: In function `main':
b_t.cpp:(.text+0x16e): undefined reference to `tree::inorder()'
/tmp/ccAjhirw.o: In function `tree::tree()':
b_t.cpp:(.text._ZN4treeC2Ev[_ZN4treeC5Ev]+0x9): undefined reference to `vtable for tree'
/tmp/ccAjhirw.o: In function `tree::tree(tree const&)':
b_t.cpp:(.text._ZN4treeC2ERKS_[_ZN4treeC5ERKS_]+0xd): undefined reference to `vtable for tree'
/tmp/ccAjhirw.o: In function `non_emp_tree::inorder()':
b_t.cpp:(.text._ZN12non_emp_tree7inorderEv[_ZN12non_emp_tree7inorderEv]+0x19): undefined reference to `tree::inorder()'
b_t.cpp:(.text._ZN12non_emp_tree7inorderEv[_ZN12non_emp_tree7inorderEv]+0x56): undefined reference to `tree::inorder()'
/tmp/ccAjhirw.o: In function `tree::tree(tree&&)':
b_t.cpp:(.text._ZN4treeC2EOS_[_ZN4treeC5EOS_]+0xd): undefined reference to `vtable for tree'
/tmp/ccAjhirw.o:(.rodata._ZTI12non_emp_tree[_ZTI12non_emp_tree]+0x10): undefined reference to `typeinfo for tree'
/tmp/ccAjhirw.o:(.rodata._ZTI8emp_tree[_ZTI8emp_tree]+0x10): undefined reference to `typeinfo for tree'
collect2: error: ld returned 1 exit status

Edit: I changed virtual void inroder() to virtual void inorder() {} i.e empty implementation. But still I am not getting desired output, it seems root, leaf1 and leaf2 are both calling tree's inorder not their respective inorders.

Abhishek Kumar
  • 729
  • 6
  • 20
  • Possible duplicate of [What is an undefined reference/unresolved external symbol error and how do I fix it?](http://stackoverflow.com/questions/12573816/what-is-an-undefined-reference-unresolved-external-symbol-error-and-how-do-i-fix) – Rakete1111 Apr 05 '17 at 18:20
  • 1
    @Rakete1111 I don't believe it is. Or, to be more precise, I think the OP's problem is perpendicular to the issue described in that question. – Xirema Apr 05 '17 at 18:22
  • 1
    @Xirema If the issue is described in that FAQ question, then why isn't it a duplicate? – Rakete1111 Apr 05 '17 at 18:25
  • 1
    @Rakete1111 Because an undefined reference isn't the OP's "problem", it's a symptom of their real problem. The OP's problem is that they're trying to invoke polymorphism in their code, but they aren't using the correct constructs to represent polymorphic objects in their class structure. Fixing the "undefined reference" problem would not fix their overall problem, it would just change the kind of error they get (probably from a compiler error to a runtime error). – Xirema Apr 05 '17 at 18:28
  • Possible duplicate of [Undefined reference to vtable](http://stackoverflow.com/questions/3065154/undefined-reference-to-vtable) – Zulan Apr 05 '17 at 19:16

5 Answers5

3

You never implemented tree::inorder.

class tree{
public:
    virtual void inorder();
};

Here you claimed there was such a function -- where is its implementation?

Also, this doesn't make sense:

tree leaf1 = non_emp_tree(1, emp_tree(), emp_tree());

You're setting a tree's value equal to a non_emp_tree's value. That certainly won't do anything useful.

David Schwartz
  • 179,497
  • 17
  • 214
  • 278
  • 3
    I'd appreciate an explanation from the downvoter. – David Schwartz Apr 05 '17 at 18:20
  • 3
    I didn't down vote, but but your answer is incorrect. the code is slicing and not using virtual dispatch. Note there are no pointers. The OPs code looks like it is trying to use virtual calls, but forgotten they need to be pointers. – Richard Critten Apr 05 '17 at 18:22
  • @RichardCritten His answer is one of the problems with the code. – AresCaelum Apr 05 '17 at 18:23
  • 4
    @RichardCritten The question asks why the code doesn't compile. – David Schwartz Apr 05 '17 at 18:23
  • When troubleshooting code you always troubleshoot the first error, in this case the first error deals with `tree::inorder()` not being implemented, once the function is implemented the rest of the errors go away. – AresCaelum Apr 05 '17 at 18:27
1

virtual void inorder(){} should be in your tree class you are missing {}

Mohit Yadav
  • 471
  • 8
  • 17
  • 1
    Either `{}` or `= 0;` would fix it; given that he's probably trying to implement polymorphism with `tree` as an abstract base class, the latter would make more sense. – Quietust Apr 05 '17 at 18:33
  • yup you are right.Thanks for mentioning that – Mohit Yadav Apr 05 '17 at 18:36
  • @Quietust if he defined it with `= 0;` he would have a new compiler error since the children dont override that function, in order to use the `= 0;` he would need to mark the child classes with virtual on the inorder function. He also wouldn't be able to make an instance of the tree class. So the 3 variables in his main would error. – AresCaelum Apr 05 '17 at 18:36
  • @Eddge good point, overlooked that. – Quietust Apr 05 '17 at 18:38
1

What you presumably tried to do was to use polymorphism. However, for that you must use a reference or pointer to base not a base itself. I.e.

#include<iostream>
#include<memory>

struct tree
{
  virtual void inorder() = 0;  // abstract: cannot be called
  virtual ~tree() {}
};

struct emp_tree : tree
{
  void inorder() override
  {}
};

struct non_emp_tree : tree
{
  std::unique_ptr<tree> left,right; 
  int val;
  non_emp_tree(int v, tree*l, tree*r)
  : left(l), right(r), val(v) {}
  void inorder() override
  {
    left->inorder();
    std::cout<<" "<<val<<" ";
    right->inorder();
  }
};

int main() {
  auto leaf1 = new non_emp_tree(1, new emp_tree, new emp_tree);
  auto leaf2 = new non_emp_tree(3, new emp_tree, new emp_tree);
  auto root  = new non_emp_tree(2, leaf1, leaf2);
  root->inorder();
}

compiled with clang, produces 1 2 3.

Walter
  • 44,150
  • 20
  • 113
  • 196
  • Above code doesn't compile, though `virtual void inorder() {};` compiles and behaves fine. Can you explain? – Abhishek Kumar Apr 05 '17 at 18:43
  • @Walter You need to add a defaulted `virtual` destructor to `tree`, or the cleanup won't work correctly. – Xirema Apr 05 '17 at 18:44
  • the code **does** compile, I did it. You presumably are missing the relevant system headers. I'll add them in edit. – Walter Apr 05 '17 at 18:44
  • @Xirema good point. I will change the code. – Walter Apr 05 '17 at 18:44
  • @AbhishekKumar I was able to compile their code with no changes. http://ideone.com/eeKdkA – Xirema Apr 05 '17 at 18:45
  • Yeah, it is compiling. I was doing some mistake. Thanks Still, can someone tell me difference b/w =0; and {}; ? – Abhishek Kumar Apr 05 '17 at 18:51
  • @AbhishekKumar `=0` explicitly tells the compiler "a base object of this type cannot call this method" (which basically implies that you cannot create objects of that type) whereas `{}` defines an empty body for the function, meaning "a base object can call this method, and the method does nothing" (which implies that you can create objects of that type). My advice is that you [find a good C++ book](http://stackoverflow.com/questions/388242/the-definitive-c-book-guide-and-list) and do some reading, since you may need to review the fundamentals. – Xirema Apr 05 '17 at 18:55
1

First things first, if tree::inorder is meant to be an abstract method, you need to properly declare it as such:

class tree{
public:
    virtual void inorder() = 0;
};

However, this is very quickly going to lead to a bunch of other problems, because you're slicing up your objects left and right!

tree leaf1 = non_emp_tree(1, emp_tree(), emp_tree());

This code cannot possibly have the effect you intend. non_emp_tree contains member variables that a tree object doesn't have room to store. And even if it did, you'd have no guarantee that the object would behave the way you expect. Invocations of inorder on leaf1 would attempt to call tree::inorder, not non_emp_tree::inorder, because the program has no way of knowing that you intended to store a subclass here.

The way to fix this is to use pointers for all your tree objects.

#include<iostream>
#include<memory>

class tree{
public:
    virtual void inorder() = 0;
    virtual ~tree() = default;
};

class emp_tree: public tree{
public:
    void inorder(){
    }
};

class non_emp_tree: public tree{
public:
    std::unique_ptr<tree> left, right;
    int val;
    non_emp_tree(int v, tree *l, tree *r): val(v), left(l), right(r) {};
    void inorder(){
        if(left) left->inorder();
        std::cout<<" "<<val<<" ";
        if(right) right->inorder();
    }
};

int main() {
    std::unique_ptr<tree> leaf1 = std::make_unique<non_emp_tree>(1, new emp_tree, new emp_tree);
    std::unique_ptr<tree> leaf2 = std::make_unique<non_emp_tree>(3, new emp_tree, new emp_tree);
    std::unique_ptr<tree> root = std::make_unique<non_emp_tree>(2, leaf1.release(), leaf2.release());
    root->inorder();
    return 0;
}

A better implementation of this code would avoid any uses of naked new at all, but that would require some significant refactoring of your code.

Xirema
  • 19,889
  • 4
  • 32
  • 68
0

The "inorder" method of class "tree" is not implemented, so you get an undefined reference to this method when calling it.

gaFF
  • 747
  • 3
  • 11