2

I have a virtual base class, called AbstractHeap. AbstractHeap has a virtual function, "comp". It also has a (non-virtual) constructor and destructor that should be used by all derived classes, which are implemented.

AbstractHeap is inherited by MinHeap. It implements "comp". It also has a constructor, which just calls the base constructor.

Finally, Main.cpp just creates an instance of MinHeap.

When I compile the program, it gives me this error:

$ rm *.o && make
g++ -std=c++11 -c -g AbstractHeap.cpp
g++ -std=c++11 -c -g Main.cpp
g++ -std=c++11 -g -Wall Main.o AbstractHeap.o -o Main
Main.o: In function `MinHeap::MinHeap(int, int)':
/mnt/c/dev/Data_Structures/Lab7/MinHeap.h:8: undefined reference to `vtable for MinHeap'
/mnt/c/dev/Data_Structures/Lab7/MinHeap.h:8: undefined reference to `vtable for MinHeap'
collect2: error: ld returned 1 exit status
Makefile:2: recipe for target 'Main' failed
make: *** [Main] Error 1
owen@MatrixSword:/mnt/c/dev/Data_Structures/Lab7$ rm *.o && make
g++ -std=c++11 -c -g AbstractHeap.cpp
g++ -std=c++11 -c -g Main.cpp
g++ -std=c++11 -g -Wall Main.o AbstractHeap.o -o Main
Main.o: In function `MinHeap::MinHeap(int, int)':
MinHeap.h:8: undefined reference to `vtable for MinHeap'
MinHeap.h:8: undefined reference to `vtable for MinHeap'
Main.o: In function `MinHeap::~MinHeap()':
MinHeap.h:5: undefined reference to `vtable for MinHeap'
MinHeap.h:5: undefined reference to `vtable for MinHeap'
collect2: error: ld returned 1 exit status
Makefile:2: recipe for target 'Main' failed
make: *** [Main] Error 1

I've looked at this highly upvoted question, but it seems that the problem and solution involved QT, which I am not using.

Here's the code.

AbstractHeap.h

#ifndef ABSTRACT_HEAP_H
#define ABSTRACT_HEAP_H
#include <iostream>
#include <string>
using namespace std;

struct Item
{
    int key = -1;
    string data = "";
};

class AbstractHeap
{
    private:
        int k; //A k-heap.
        int size;
        Item* A; //The array.

    public:
        AbstractHeap(int k, int size);
        ~AbstractHeap();

        //Comparison operator. Can be interpretted as c comp b. So, if you want a <, comp is a<b. If the result is true, a has a higher priority than b.
        virtual bool comp(int a, int b) = 0;
};

#endif

AbstractHeap.cpp

#include "AbstractHeap.h"
#include <iostream>
#include <algorithm>
using namespace std;

AbstractHeap::AbstractHeap(int _k, int _size)
{
    size = _size;
    k = _k;
    A = new Item[size];
}

AbstractHeap::~AbstractHeap()
{
    delete [] A;
}

MinHeap.h

#ifndef MIN_HEAP_H
#define MIN_HEAP
#include "AbstractHeap.h"

class MinHeap : virtual public AbstractHeap
{
    public:
        MinHeap(int k, int size) : AbstractHeap{k, size} {};
        bool comp(int a, int b);
};

#endif

MinHeap.cpp

#include "MinHeap.h"

bool MinHeap::comp(int a, int b)
{
    return a < b;
}

Finally, here's my Makefile, in the unlikely event it is a result of poor Makefile writing.

Main: AbstractHeap.o Main.o 
    g++ -std=c++11 -g -Wall Main.o AbstractHeap.o -o Main

Main.o: Main.cpp
    g++ -std=c++11 -c -g Main.cpp

AbstractHeap.o: AbstractHeap.cpp AbstractHeap.h MinHeap.cpp MinHeap.h
    g++ -std=c++11 -c -g AbstractHeap.cpp

EDIT: The problem is fixed! The problem was actually three problems.

  1. I removed "virtual" in the declaration of MinHeap. ("class MinHeap : public AbstractHeap")

  2. I added "virtual" in front of the destructor in AbstractHeap. ("virtual ~AbstractHeap")

  3. I added a compilation rule to create MinHeap.o, like so:

Main: AbstractHeap.o Main.o MinHeap.o
    g++ -std=c++11 -g -Wall Main.o AbstractHeap.o MinHeap.o -o Main

Main.o: Main.cpp
    g++ -std=c++11 -c -g Main.cpp

AbstractHeap.o: AbstractHeap.cpp AbstractHeap.h MinHeap.cpp MinHeap.h
    g++ -std=c++11 -c -g MinHeap.cpp
    g++ -std=c++11 -c -g AbstractHeap.cpp

Thanks everyone!

Owen
  • 65
  • 7
  • Also I am rather new to submitting StackOverflow questions, so be nice please :) – Owen Mar 26 '20 at 18:50
  • 3
    Not going to happen. We are the purest of evil here. Doomed! You are dooomed! Muhuhuhahahahahahahahahahaha! – user4581301 Mar 26 '20 at 19:01
  • See what happens if you change ` ~AbstractHeap();` into `virtual ~AbstractHeap();`. [You'll want to do it anyway](https://stackoverflow.com/questions/461203/when-to-use-virtual-destructors). – user4581301 Mar 26 '20 at 19:02
  • 1
    Excellent question - you provided all the information need right from the start. – Jasper Kent Mar 26 '20 at 19:03
  • I can't reproduce your issues right now, but have the following comments: With polymorphic classes, I would recommend always declaring the destructor virtual, because every derived class might have data members with destructors that might not get destroyed then. Secondly, you probably don't want *virtual inheritance*, since you don't use multiple inheritance, so I would drop the `virtual` before `public AbstractHeap`. Thirdly, I would recommend to use `std::vector` instead of manually handling your memory with `new` and `delete` and finally, your header guard for MinHeap.h seems to be broken. – Tobias Ribizel Mar 26 '20 at 19:03
  • BTW [Effective C++ -item 7 declare destructors virtual in polymorphic base classes](https://blog.ycshao.com/2012/03/18/effective-c-item-7-declare-destructors-virtual-in-polymorphic-base-classes/) – Victor Gubin Mar 26 '20 at 19:04
  • 1
    Look at your makefile. Where is the command that will compile `MinHeap.cpp`? (It should look like `g++ -std=c++11 -c -g MinHeap.cpp`.) Now cross-reference with the answers to the question that you somehow thought was all about QT: [Undefined reference to vtable](https://stackoverflow.com/questions/3065154/undefined-reference-to-vtable#8923163). *Hmm: quick test: put an obvious syntax error in `MinHeap.cpp`, then rebuild your project. Did the compiler complain?* – JaMiT Mar 26 '20 at 19:10
  • JaMit is entirely correct, thank you! – Owen Mar 26 '20 at 19:32

1 Answers1

4

Two things:

1) This code is very unlikely to be what you want:

 class MinHeap : virtual public AbstractHeap

virtual derivation is only of use when you are doing multiple inheritance (which you aren't here, from what you've shown), and even then it's best avoided.

You simply need:

class MinHeap : public AbstractHeap

I suspect this causes the error.

2) If you have a base class, its destructor should be declared as virtual, so:

virtual ~AbstractHeap();

If not, then when you delete a derived class object, the derived class destructor may be skipped. So you need a virtual destructor in the base class even if it does nothing and has an empty body, because the benefit is in the derived class.

Jasper Kent
  • 3,546
  • 15
  • 21