9

Tried searching, nothing returns( i ithink).

Is it possible to make a vector of an abstract class?

For example, I have the super class Unit.

And I Have the subclasses soldier, vehicle, and bomber.

However I would want ever instance of any subclass in one vector, e.g. the vector UnitList can hold instances of both soldier and vehicle?

Is this possible? I'm using C++ if it helps.

andrew Patterson
  • 559
  • 2
  • 6
  • 19

4 Answers4

13

Yes, but you'll need to use either pointers or smart pointers (I'd go with this).

struct X
{
    virtual ~X() {}  //<--- as pointed out in the comments
                     // a virtual destructor is required
                     // for correct deletion
    virtual void foo() = 0;
};
struct Y : X
{
    virtual void foo() { }
};

int main()
{
    std::vector<X*> a;
    a.push_back(new Y);
    a[0]->foo();
    for ( int i = 0 ; i < a.size() ; i++ )
        delete a[i];
    return 0;
}

Don't forget to delete the allocated memory.

Why you can't use actual objects:

Assume std::vector<X>. This is illegal because:

  1. If you want to initialize your vector with some number of elements, the allocation would fail. A vector stores objects internally in continuous memory. A preallocation would fail because it would mean it needed to create objects, which can't be done for abstract classes.

  2. Even if you could, or the base class wasn't abstract, it wouldn't help too much, as you'd suffer from object slicing.

Community
  • 1
  • 1
Luchian Grigore
  • 253,575
  • 64
  • 457
  • 625
  • 1
    Just a slight addition: smart pointers will require either a virtual destructor or, for `std::unique_ptr`, a stored cleanup function, to ensure that the correct destructor is called by the smart pointer. – Ben Voigt Apr 26 '12 at 17:42
10

Since each of those objects is different sizes, the proper way to do this is a container containing pointers to the base class Unit, (and be sure it has a virtual destructor.

Option 1: boost::ptr_vector (requires boost libraries, which you should have anyway)

ptr_vector<Unit> units;
units.push_back(new Soldier());

Option 2: std::vector<std::unique_ptr<Unit>> (requires C++11 compiler)

std::vector<std::unique_ptr<Unit>> units;
units.emplace_back(std::unique_ptr<Unit>(new Soldier()));
Mooing Duck
  • 64,318
  • 19
  • 100
  • 158
  • Wouldn't `units.emplace_back(new Soldier())` be better? No, wait, exception-safety is tricky. Your existing code is better, though I'd still replace `push_back` with `emplace_back`. – Ben Voigt Apr 26 '12 at 18:23
2

You cannot literally create a vector of objects of an abstract class, because the vector template needs to be able to tell the compiler, at compile time, the size of its element. However, you can get what you want by keeping a vector of pointers to objects rather than a vector of objects as such.

Update: Abstract classes as such have known size. It is actually instances of their most derived types whose sizes are unknown at compile time. (Thanks @LuchianGrigore and @MooingDuck for pointing this out.)

thb
  • 13,796
  • 3
  • 40
  • 68
  • 1
    I'm not going to downvote, but FYI, your assumption is wrong. Are you implying that abstract classes have unknown size? – Luchian Grigore Apr 26 '12 at 17:52
  • 1
    @LuchianGrigore: I think he's referring to instances of the most derived types. The sizes vary, and cannot be known at compile time. – Mooing Duck Apr 26 '12 at 17:54
  • @MooingDuck his wording is off then - "You cannot literally create a vector of objects of an abstract class, because..." - that's definetely not the reason. – Luchian Grigore Apr 26 '12 at 17:55
  • 1
    @thb: Never refer to comments, as comments can be and are deleted without warning. Put all important information into the question itself, always – Mooing Duck Apr 26 '12 at 18:06
  • @MooingDuck: Okay. When I do, how should I give Luchian credit for improving my answer? – thb Apr 26 '12 at 18:11
  • @MooingDuck: (I happen to be about to log off, but it's not because your advice is unappreciated! Should I find a message from you in my inbox next time I log on, whenever that may be, I'll react to it then.) – thb Apr 26 '12 at 18:22
  • 2
    @thb: Edit your answer with the correct information, and just include `(thanks @Luchian for pointing this out)` or similar parenthesized words. – Ben Voigt Apr 26 '12 at 18:25
2

I don't think it's possible to do this:

std::vector<Unit> unit_list;
unit_list.push_back(Soldier(params));

Because Soldier, Vehicle, and Unit take up different amounts of memory.

However, it might be possible to use pointers, as all pointers take up a fixed amount of memory:

std::vector<Unit*> unit_list;
unit_list.push_back(new Soldier(params));

I haven't tested it, but it makes sense in my head anyway.

Luchian Grigore
  • 253,575
  • 64
  • 457
  • 625
paulrehkugler
  • 3,241
  • 24
  • 45