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

After spending some time in C #, I noticed that if you declare an abstract class to use as an interface, you cannot create a vector of this abstract class to store instances of child 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; }; 

A line declaring an abstract class vector causes this error in MS VS2005:

 error C2259: 'IFunnyInterface' : cannot instantiate abstract class 

I see an obvious workaround which is to replace IFunnyInterface as follows:

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

Is this an acceptable C ++ solution? If not, is there any third-party library like boost that can help me get around this?

Thanks for reading this!

Anthony

+61
c ++ abstract-class stl
Jan 29 '10 at 9:17
source share
7 answers

You cannot create abstract classes, therefore the vector of abstract classes cannot work.

However, you can use a pointer vector for abstract classes:

 std::vector<IFunnyInterface*> ifVec; 

It also allows you to actually use polymorphic behavior - even if the class was not abstract, saving by value would lead to the problem of splitting objects .

+92
Jan 29 '10 at 9:20
source share

You cannot create a vector of an abstract type of a class because you cannot create instances of an abstract class and containers of the C ++ standard library, such as std :: vector, to store values ​​(for example, instances). If you want to do this, you will need to create a vector of pointers to the abstract type of the class.

Your workround will not work, because virtual functions (which is why you want the abstract class to be in the first place) worked only when called with pointers or links. You also cannot create link vectors, so this is the second reason why you should use a pointer vector.

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

+17
Jan 29 '10 at 9:20
source share

A traditional alternative is to use vector pointers, as already noted.

For those who appreciate it, Boost comes with a very interesting library: Pointer Containers , which is great for the task and frees you from various problems related to pointers:

  • life cycle management
  • double dereferencing iterators

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

Now there is a third option, which is to change your hierarchy. For better user isolation, I saw the following pattern several times:

 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 { .. }; 

It's pretty simple, and the Pimpl idiom Pimpl is enriched with the Strategy template.

This works, of course, only if you do not want to directly manipulate the "true" objects and mean a deep copy. Perhaps this is not how you wish.

+6
Jan 29 '10 at 16:24
source share

In this case, we will not be able to use even this code:

 std::vector <IFunnyInterface*> funnyItems; 

or

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

Since there is no ISA connection between FunnyImpl and IFunnyInterface and there is no implicit conversion between FUnnyImpl and IFunnyInterface due to 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>"; } }; 
+5
Jan 29 '10 at 10:57
source share

Since you need to use the default constructor and class size to resize the vector, which in turn requires it to be specific.

You can use a pointer like others.

+2
Jan 29 '10 at 9:22
source share

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

I think that with your workaround, you can compile vector<IFunnyInterface> , but you cannot manipulate FunnyImpl inside it. For example, if an IFunnyInterface (abstract class) is 20 (I really don't know), and FunnyImpl is 30 because it has more members and code, you will end up trying to put 30 in your vector of 20

The solution is to allocate memory on the heap using the "new" and storage pointers in vector<IFunnyInterface*>

+1
Jan 29 '10 at 9:35 on
source share

I think the main reason for this really sad limitation is the fact that constructors cannot be virtual. As a result of this, the compiler cannot generate code that copies the object without knowing its time at compile time.

-2
Jan 29 '10 at 9:35 on
source share



All Articles