Home » C++ » Why can't we declare a std::vector<AbstractClass>?

Why can't we declare a std::vector<AbstractClass>?

Posted by: admin November 29, 2017 Leave a comment

Questions:

Having spent quite some time developping in C#, I noticed that if you declare an abstract class for the purpose of using it as an interface you cannot instantiate a vector of this abstract class to store instances of the children classes.

#pragma once
#include <iostream>
#include <vector>

using namespace std;

class IFunnyInterface
{
public:
    virtual void IamFunny()  = 0;
};

class FunnyImpl: IFunnyInterface
{
public:
    virtual void IamFunny()
    {
        cout << "<INSERT JOKE HERE>";
    }
};

class FunnyContainer
{
private:
    std::vector <IFunnyInterface> funnyItems;
};

The line declaring the vector of abstract class causes this error in MS VS2005:

error C2259: 'IFunnyInterface' : cannot instantiate abstract class

I see an obvious workaround, which is to replace IFunnyInterface with the following:

class IFunnyInterface
{
public:
    virtual void IamFunny()
    {
        throw new std::exception("not implemented");
    }
};

Is this an acceptable workaround C++ wise ?
If not, is there any third party library like boost which could help me to get around this ?

Thank you for reading this !

Anthony

Answers:

You can’t instantiate abstract classes, thus a vector of abstract classes can’t work.

You can however use a vector of pointers to abstract classes:

std::vector<IFunnyInterface*> ifVec;

This also allows you to actually use polymorphic behaviour – even if the class wasn’t abstract, storing by value would lead to the problem of object slicing.

Questions:
Answers:

You can’t create a vector of an abstract class type because you cannot create instances of an abstract class, and C++ Standard Library containers like std::vector store values (i.e. instances). If you want to do this, you will have to create a vector of pointers to the abstract class type.

Your workround would not work because virtual functions (which is why you want the abstract class in the first place) only work when called through pointers or references. You cannot create vectors of references either, so this is a second reason why you must use a vector of pointers.

You should realise that C++ and C# have very little in common. If you are intending to learn C++, you should think of it as starting from scratch, and read a good dedicated C++ tutorial such as Accelerated C++ by Koenig and Moo.

Questions:
Answers:

The traditional alternative is to use a vector of pointers, like already noted.

For those who appreciate, Boost comes with a very interesting library: Pointer Containers which is perfectly suited for the task and frees you from the various problems implied by pointers:

  • lifetime management
  • double dereferencing of iterators

Note that this is significantly better than a vector of smart pointers, both in terms of performance and interface.

Now, there is a 3rd alternative, which is to change your hierarchy. For better insulation of the user, I have seen a number of times the following pattern used:

class IClass;

class MyClass
{
public:
  typedef enum { Var1, Var2 } Type;

  explicit MyClass(Type type);

  int foo();
  int bar();

private:
  IClass* m_impl;
};

struct IClass
{
  virtual ~IClass();

  virtual int foo();
  virtual int bar();
};

class MyClass1: public IClass { .. };
class MyClass2: public IClass { .. };

This is quite straightforward, and a variation of the Pimpl idiom enriched by a Strategy pattern.

It works, of course, only in the case where you do not wish to manipulate the “true” objects directly, and involves deep-copy. So it may not be what you wish.

Questions:
Answers:

In this case we can’t use even this code:

std::vector <IFunnyInterface*> funnyItems;

or

std::vector <std::tr1::shared_ptr<IFunnyInterface> > funnyItems;

Because there is no IS A relationship between FunnyImpl and IFunnyInterface and there is no implicit convertion between FUnnyImpl and IFunnyInterface because of private inheritance.

You should update your code as follows:

class IFunnyInterface
{
public:
    virtual void IamFunny()  = 0;
};

class FunnyImpl: public IFunnyInterface
{
public:
    virtual void IamFunny()
    {
        cout << "<INSERT JOKE HERE>";
    }
};

Questions:
Answers:

Because to resize a vector you need to use the default constructor and the size of the class, which in turn requires it to be concrete.

You can use a pointer as other suggested.

Questions:
Answers:

std::vector will try to allocate memory to contain your type. If your class is purely virtual, the vector cannot know the size of the class it will have to allocate.

I think that with your workaround, you will be able to compile a vector<IFunnyInterface> but you won’t be able to manipulate FunnyImpl inside of it. For example if IFunnyInterface (abstract class) is of size 20 (i dont really know) and FunnyImpl is of size 30 because it has more members and code, you will end up trying to fit 30 into your vector of 20

The solution would be to allocate memory on the heap with “new” and store pointers in vector<IFunnyInterface*>

Questions:
Answers:

I think that the root cause of this really sad limitation is the fact that constructors can not virtual. Thereof compiler can not generate code which copy the object without knowing its time in the compile time.