Search code examples
c++virtual-functions

Overriding pure virtual functions


I've been trying to figure out why I get an error when I try to build this code in QTCreator. I found some other posts similar to this but I think I have a similar but different issue.

In main.cpp I basically have some Filter (a templated struct with a pure virtual function) that I'm creating that returns to me a vector of MyProducts. I give the filter a Specification (a templated struct with a pure virtual function) and a vector of MyProduct to search through.

But I keep getting this error message during the build.

/Users/marcokok/Qt-workspace/Open_Closed_Principle/main.cpp:112: error: allocating an object of abstract class type 'Specification<MyProduct>'
/Users/marcokok/Qt-workspace/Open_Closed_Principle/main.cpp:112:36: error: allocating an object of abstract class type 'Specification<MyProduct>'
    AndSpecification<MyProduct> as(cs, ss);
                                   ^
/Users/marcokok/Qt-workspace/Open_Closed_Principle/main.cpp:29:18: note: unimplemented pure virtual method 'is_satisfied' in 'Specification'
    virtual bool is_satisfied(T* item) = 0;
                 ^

Here is my source code

main.cpp

#include <iostream>
#include <vector>

using namespace std;

enum class ColorTable
{
    red,
    green,
    blue
};

enum class SizeTable
{
    small,
    medium,
    large
};

struct MyProduct
{
    string name;
    ColorTable color;
    SizeTable size;
};

template <typename T> struct Specification
{
    virtual bool is_satisfied(T* item) = 0;
};

template <typename T> struct Filter
{
    virtual vector<T*> filter(vector<T*> items, Specification<T>& spec) = 0;
};

struct BetterFilter : Filter<MyProduct>
{
    vector<MyProduct*> filter(vector<MyProduct *> items, Specification<MyProduct> &spec) override
    {
        vector<MyProduct*> result;

        for (auto& item : items)
        {
            if (spec.is_satisfied(item))
                result.push_back(item);
        }
        return result;
    }
};

struct ColorSpecification : Specification<MyProduct>
{
    ColorTable color;

    ColorSpecification(ColorTable color) : color(color)
    {}

    bool is_satisfied(MyProduct* item) override
    {
        if (item->color == color)
            return true;
        else
            return false;
    }
};

struct SizeSpecification : Specification<MyProduct>
{
    SizeTable size;

    SizeSpecification(SizeTable size) : size(size)
    {}

    bool is_satisfied(MyProduct* item) override
    {
        if (item->size == size)
            return true;
        else
            return false;
    }
};

template <typename T> struct AndSpecification : Specification<T>
{
    Specification<T>& first;
    Specification<T>& second;

    AndSpecification(Specification<T> first, Specification<T> second) : first(first) , second(second)
    {}

    bool is_satisfied(T* item) override
    {
        if (first.is_satisfied(item) && second.is_satisfied(item))
            return true;
        else
            return false;
    }

};
int main()
{
    MyProduct prod_1{"Apple", ColorTable::green, SizeTable::large};
    MyProduct prod_2{"Jeans", ColorTable::green, SizeTable::small};
    MyProduct prod_3{"Graphics Card", ColorTable::blue, SizeTable::large};

    vector<MyProduct*> items = {&prod_1, &prod_2, &prod_3};

    BetterFilter bf;
    ColorSpecification cs(ColorTable::green);
    SizeSpecification ss(SizeTable::large);
    AndSpecification<MyProduct> as(cs, ss);

    auto green_things = bf.filter(items, cs);
    for (auto& item : green_things)
        cout << item->name << " is green." << endl;

    auto large_things = bf.filter(items, ss);
    for (auto& item : large_things)
        cout << item->name << " is large.\n\n" << endl;

   // auto large_green_things = bf.filter(items, as);
    //for (auto& item : large_green_things)
     //   cout << item->name << " is large and green." << endl;
    return 0;
}

Solution

  • Your compiler tells you where the error occurs.

    /Users/marcokok/Qt-workspace/Open_Closed_Principle/main.cpp:112:36: error: allocating an object of abstract class type 'Specification<MyProduct>'
       AndSpecification<MyProduct> as(cs, ss);
                                      ^
    

    The attempt to instantiate Specification<MyProduct> occurs when using cs as the first argument to the AndSpecification<MyProduct> constructor. Creating cs was fine, but somehow this function call triggers the construction of an abstract class. So let's look at the declaration of that constructor.

       AndSpecification(Specification<T> first, Specification<T> second) 
    

    The first parameter is passed by value (unlike many of your other parameters). This means that a newly-constructed Specification<MyProduct> object is initialized from cs. (The Specification<MyProduct> sub-object of cs is looked at, and those data members are copied to the new object.) However, Specification<MyProduct> is abstract (as are all Specification<T> classes), so it cannot be constructed. Hence the error.

    Change your constructor to accept arguments by reference.

            AndSpecification(Specification<T>& first, Specification<T>& second) 
            //                               ^                        ^
    

    See? It's not as complicated as you made it appear, if you know how to read the error message.