0

When building my C++ program, I'm getting the error message.

undefined reference to vtable

I have two virtual abstract classes called and I can't quite figure out what I'm doing wrong if I'm doing anything wrong. I'm getting errors from both of the classes that are inheriting from the abstract class. My abstract class is

undefined reference to `vtable for hittable_list'

undefined reference to `vtable for sphere'

hittable.h

     #ifndef HITTABLE_H
        #define HITTABLE_H
    
    #include "ray.h"
    
    struct hit_record {
        hit_record() {}
        ~hit_record() {}
        float t;
        vecfloat p;
        vecfloat normal;
        float MAXFLOAT = 100.0; 
}; 

    //Abstract Class containing Sphere and hittablelist 
    class hittable 
    {
     public:
            virtual ~hittable() = 0;
        
            virtual bool hit(const ray &r, float t_min, float t_max, hit_record &rec) const = 0;
 };
        
        #endif

Classes inherting from my abstract class are.

sphere.h

 #ifndef SPHERE_H
#define SPHERE_H

#include "hittable.h"

class sphere : public hittable
{
public:
    sphere() {}
    ~sphere() {}
    sphere(vecfloat cen, float r) : center(cen), radius(r) {}
    bool hit(const ray &r, float t_min, float t_max, hit_record &rec) const;

protected:
    vecfloat center;
    float radius;
};

#endif

sphere.cc

#include "include/sphere.h"

bool sphere::hit(const ray &r, float t_min, float t_max, hit_record &rec) const
{
    vecfloat oc = r.origin() - center;
    float a = oc.dot_product(r.direction());
    float b = oc.dot_product(oc) - radius * radius;
    float c = oc.dot_product(oc) - radius * radius;
    float discriminant = b * b - a * c;
    if (discriminant > 0)
    {
        float temp = (-b - sqrt(b * b - a * c)) / a;
        if (temp < t_max && temp > t_min)
        {
            rec.t = temp;
            rec.p = r.point_at_parameter(rec.t);
            rec.normal = (rec.p - center) / radius;
            return true;
        }
        temp = (-b + sqrt(b * b - a * c)) / a;
        if (temp < t_max && temp > t_min)
        {
            rec.t = temp;
            rec.p = r.point_at_parameter(rec.t);
            rec.normal = (rec.p - center) / radius;
            return true;
        }
    }
    return false;
}

hittable.h

#ifndef HITTABLELIST_H
#define HITTABLELIST_H
#include "hittable.h"

class hittable_list : public hittable
{
public:
    hittable_list() {}

    hittable_list(hittable **l, int n)
    {
        list = l;
        list_size = n;
    }
    bool hit(const ray &r, float t_min, float t_max, hit_record &rec) const;
    ~hittable_list() {}

protected:
    hittable **list;
    int list_size;
};

#endif

hittable.cc

#include "include/hittablelist.h"

bool hittable_list::hit(const ray &r, float t_min, float t_max, hit_record &rec) const
{

    hit_record temp_rec;
    auto hit_anything = false;
    auto closet_so_far = t_max;
    for (int i = 0; i < list_size; i++)
    {
        if (list[i]->hit(r, t_min, closet_so_far, temp_rec))
        {
            hit_anything = true;
            closet_so_far = temp_rec.t;
            rec = temp_rec;
        }
    }
    return hit_anything;
}
7
  • I've never even thought to make a pure virtual destructor. Wonder what happens and it it's related. Commented Aug 22, 2020 at 3:56
  • Yeah, that'll be it. Lemme shrink the code down to a minimal reproducible example and write it up. Commented Aug 22, 2020 at 3:59
  • Huh.Can't reproduce the undefined vtable Commented Aug 22, 2020 at 4:06
  • 4
    Does this answer your question? Undefined reference to vtable, specifically the answers that mention checking your link command. Commented Aug 22, 2020 at 4:09
  • @JaMiT That will do it, but mostly by accident.The kicker here is the hittable destructor must be defined to solve all of the problems with this code. It can stay pure virtual, but it must be defined. Classes derived from it must have something to call. Commented Aug 22, 2020 at 4:13

1 Answer 1

1

Solution

Change

virtual ~hittable() = 0; 

into

virtual ~hittable() = default; 

or

virtual ~hittable()
{
    // does nothing
} 

The destructor can remain pure virtual, but it must have a definition.

hittable::~hittable()
{
    // does nothing
}

So what happened? We can crush the given code down to the following example (Note I can't reproduce the missing vtable with this code or the given code, but regardless, the above will fix it)

Minimal example:

class hittable
{
public:
    virtual ~hittable() = 0; // here we have a destructor, 
                             // but there's no implementation 
                             // so there is nothing for the
                             // destructors of derived classes 
                             // to call  

    virtual bool hit() const = 0;
};

class sphere: public hittable
{
public:
    bool hit() const;

};

bool sphere::hit() const
{
    return false;
}

destructors call any base class destructors, and when shpere goes to call ~hittable, it finds that while ~hittableis declared, there is no implementation. Nothing to call.

Sign up to request clarification or add additional context in comments.

3 Comments

This still gives me the same error for some reason.
Something else going on. Could we get a minimal reproducible example?
Ok, it works when the file is in the header instead of a separate cc file.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.