3

Why can't I use an abstract class like an interface at runtime.

I get the output:

1>c:\program files (x86)\microsoft visual studio 12.0\vc\include\xmemory0(615): error C2259: 'Creature' : cannot instantiate abstract class
1>          due to following members:
1>          'std::string Creature::Move(std::vector<std::string,std::allocator<_Ty>> &)' : is abstract
1>          with
1>          [
1>              _Ty=std::string
1>          ]

1>          visual studio 2013\projects\cpp_demo\cpp_demo\creature.h(9) : see declaration of 'Creature::Move'
1>          c:\program files (x86)\microsoft visual studio 12.0\vc\include\xmemory0(614) : while compiling class template member function 'void std::allocator<_Ty>::construct(_Ty *,const _Ty &)'
1>          with
1>          [
1>              _Ty=Creature
1>          ]

1>          c:\program files (x86)\microsoft visual studio 12.0\vc\include\xmemory0(752) : see reference to function template instantiation 'void std::allocator<_Ty>::construct(_Ty *,const _Ty &)' being compiled
1>          with
1>          [
1>              _Ty=Creature
1>          ]

1>          c:\program files (x86)\microsoft visual studio 12.0\vc\include\type_traits(580) : see reference to class template instantiation 'std::allocator<_Ty>' being compiled
1>          with
1>          [
1>              _Ty=Creature
1>          ]

1>          c:\program files (x86)\microsoft visual studio 12.0\vc\include\vector(650) : see reference to class template instantiation 'std::is_empty<_Alloc>' being compiled
1>          with
1>          [
1>              _Alloc=std::allocator<Creature>
1>          ]

1>          visual studio 2013\projects\cpp_demo\cpp_demo\main.cpp(7) : see reference to class template instantiation 'std::vector<Creature,std::allocator<_Ty>>' being compiled
1>          with
1>          [
1>              _Ty=Creature
1>          ]

My code:

int main()
{
    unique_ptr<vector<Creature>> pCreatures(new vector<Creature>);

    unique_ptr<Creature> pHuman(new Human());
    pCreatures->push_back(*pHuman);
}



#include "stdafx.h"
#include "Creature.h"

class Human : public Creature
{
public:
    virtual string Move(vector<string> &log);
};



#include "stdafx.h"
#include "IMove.h"

class Creature : public IMove
{
public:
    virtual string Move(vector<string> &log) = 0;
    virtual string GetState(vector<string> &log);
};

Please help.

us2012
  • 16,083
  • 3
  • 46
  • 62
Scott Nimrod
  • 11,206
  • 11
  • 54
  • 118

2 Answers2

7

You CAN use abstract class in vector or unique_ptr, e.g.

#include <vector>
#include <memory>

using namespace std;

class Interface {
 public:
  virtual ~Interface() = 0;
};

Interface::~Interface() {}

class Implementation : public Interface {
};

int main(int argc, char** argv) {
  unique_ptr<Interface> p(new Implementation);
  vector<unique_ptr<Interface>> v;
  v.emplace_back(new Implementation);
  vector<Interface> vi;
  // This leads to compile error: vi.emplace_back();
}

Moreover, you CAN use vector<Interface> as long as you don't call any methods that potentially calls new Interface. For example, if you just declare a variable vector<Interface> v; it compiles, but if you push_back or emplace_back or resize, then it will have compile error because they will call new Interface.

The above code is tested under gcc-4.6.3.

Kan Li
  • 8,557
  • 8
  • 53
  • 93
  • 3
    Trying to store a derived class in `vector` will fail, however. A container that can never contain anything isn't much use. – Jonathan Potter Oct 09 '13 at 02:44
  • @JonathanPotter If you try to store a derived object in `vector`, then you will have the object truncated. So my suggestion is, never do that. This is C++, it is not Java. The vector memory buffer stores the object itself, not their pointers. – Kan Li Oct 09 '13 at 02:48
  • So how do I store different derived classes in a collection that stores a common interface? – Scott Nimrod Oct 09 '13 at 10:43
  • @ScottNimrod, as I said, use `unique_ptr` (preferred under most cases) or `shared_ptr` (which is usually unnecessary). Please read my answer first. – Kan Li Oct 10 '13 at 00:49
  • 1
    Jonathan is right then. Having a vector that cannot leverage an interface for utilizing polymorphism is not ideal. – Scott Nimrod Oct 10 '13 at 01:54
  • How can one implement the Observer Pattern using a collection of derived types in c++ then? – Scott Nimrod Oct 10 '13 at 01:57
1

You can use, but instead of using:

unique_ptr<vector<Creature>> pCreatures(new vector<Creature>);

use

vector<unique_ptr<Creature>> pCreatures;

so you will have a vectors of Creatures pointers, managed by unique_ptr.

There are, at least, two ways to use this vector:

  1. Creating the objects directly into the vector:

    pCreatures.emplace_back(new Human());

  2. Moving an unique_ptr to it:

    unique_ptr pHuman(new Human());

    pCreatures.push_back(move(pHuman));

Below is a compact usage:

int main()
{
  vector<unique_ptr<Creature>> pCreatures;

  pCreatures.emplace_back(new Human());

  unique_ptr<Creature> pHuman(new Human());
  pCreatures.push_back(move(pHuman));

  // example of usage
  pCreatures[0]->Move();
}
Amadeus
  • 10,199
  • 3
  • 25
  • 31
  • 2
    ALWAYS USE `new Human` instead of `new Human()`. See http://stackoverflow.com/questions/620137/do-the-parentheses-after-the-type-name-make-a-difference-with-new. The difference is `new Human()` zero initialize fields that are not initialized by constructor, and therefore is slower. Also it will hide bugs in your constructor. If the correctness of your constructor depends on how caller calls it (`new Human()` vs `new Human`) then it is a bug. So good written constructor should always produce valid object. Therefore `new Human` should always be used. – Kan Li Oct 11 '13 at 01:15
  • as you said, there are difference in some cases, and in these cases, `new X` is faster than `new X()` and helps unhide the bugs of forgetting initialization. So `new X` is always not-worse than `new X()` and in some cases better. Why not have the habit of always using `new X` instead of `new X()`? – Kan Li Oct 11 '13 at 05:30
  • BTW: your claim is VERY wrong that 'POD members uninitialized' only when 'a struct is composed only of POD or has a constructor generated by compiler'. In a non-POD struct, if you have POD field `m`, but omit `m()` in user-defined constructor, it is UNINITIALIZED. In C++ where performance is important, nothing is initialized unless you specify it explicitly (`new X()` or `m()` are also explicit ways to tell compiler you want to initialize it). Forget about the assumption compiler will initialize members for you. Also forget the `new X()`, but use C++ way of `new X`. This is C++, not Java. – Kan Li Oct 11 '13 at 05:37
  • The above comment says in non-POD struct, POD members `m` will be uninitialized if it has user-defined default constructor but omit `m()` in the default constructor. Although in this case, `new X()` and `new X` makes no difference. I just want to correct your idea that uninitialized members are uncommon, in fact, it is very common in C++. So that's why it is very important to catch the bug of uninitialized members. And having the habit of `new X` instead of `new X()` helps that. – Kan Li Oct 11 '13 at 05:44
  • Look aside the difference of allocating on stack or on heap, `new X()` is equivalent to `X x{};` while `new X` is equivalent to `X x;`. So if you insist writing `new X()` by default, just imagine you write `X x{};` instead of `X x;` everywhere. Well, I am not your teacher or your boss or your colleague, I don't have the duty to force you do the correct thing. You are on your own, I am just a volunteer answering questions. – Kan Li Oct 11 '13 at 06:00
  • having compiler do the implicit initialization for you is ALWAYS BAD IDEA. You need to explicitly initialize them, by constructors. If your program can't compiler using `-Wall`, it is likely your program is badly written. You should fix the root cause, i.e. forgot to initialize certain fields in constructor, rather than using `new X()` to let compiler do it for you. – Kan Li Oct 11 '13 at 21:52