Very often, when I program, I use polymorphism because it naturally models the objects that I need. On the other hand I very often use standard containers to store these objects, and I tend to avoid pointers because this either requires me to free up the objects instead of popping them off the stack or requires me to know for sure the objects will stay on the stack while I use the pointer. Of course there are all kinds of pointer-container objects that sort of do this task for you, but in my experience they are also not ideal or even annoying. That is; if such a simple solution existed, it would have been in the c++ language, right ? ;)
So lets have a classic example:
#include <iostream>
#include <vector>
struct foo {};
struct goo : public foo {};
struct moo : public foo {};
int main() {
std::vector<foo> foos;
foos.push_back(moo());
foos.push_back(goo());
foos.push_back(goo());
foos.push_back(moo());
return 0;
}
See: http://ideone.com/aEVoSi . This works fine, and if the objects have different sizeof's the compiler may apply slicing. However, due to the the fact that c++ knows no instanceof like Java, and to the best of my knowledge no adequate alternative exists, one cannot access the properties of the inherited classes after fetching them as a foo from the vector.
Hence one would use virtual function, however this disallows one to allocate a foo, and hence one is not permitted to use them in a vector. See Why can't we declare a std::vector<AbstractClass>? .
For example I may want to be able to print both subclasses, simple feature, right?
#include <iostream>
#include <vector>
struct foo {
virtual void print() =0;
virtual ~foo() {}
};
struct goo : public foo {
int a;
void print() { std::cout << "goo"; }
};
struct moo : public foo {
int a,b;
void print() { std::cout << "moo"; }
};
int main() {
std::vector<foo> foos;
foos.push_back(moo());
foos.push_back(goo());
foos.push_back(goo());
foos.push_back(moo());
for(foo& f : foos) {
f.print();
}
return 0;
}
Source: http://ideone.com/I4rYn9
This is a simple addition, as a designer I would never think of wanting this behavior in foresight. I would already be so thrilled by the fact that c++ was able to slice my objects and hence store objects of different sizes in one vector. Unfortunately it cannot do so anymore when the base class is abstract, as stated here: Why can't we declare a std::vector<AbstractClass>?
The general good solution seems to be to use pointers. But this (1) forces me to do memory management and (2) I'd need to change interfaces and recode a lot of things. For instance, consider that I first had some class interface returning a std::vector<foo>, now it returns a std::vector<foo *>, so I need to check and change all the calls of foo; which is annoying, or even impossible if I am writing a library.
So basically, imho, that is a small feature addition with big code consequences.
My question is w.r.t. coding standards. How can I prevent that these annoyances occur? Should I always use pointers, and do all my memory management? Should I always assume a class might become abstract along the way?
EDIT, ANSWER: Based on the answer of 40two I made this sniplet:
#include <iostream>
#include <vector>
#include <memory>
struct foo {
virtual void print() =0;
};
struct goo : public foo {
int a;
void print() { std::cout << "goo"; }
};
struct moo : public foo {
int a,b;
void print() { std::cout << "moo"; }
};
typedef std::unique_ptr<foo> foo_ptr;
int main() {
std::vector<std::unique_ptr<foo> > foos;
foos.push_back(foo_ptr(new moo));
foos.push_back(foo_ptr(new goo));
foos.push_back(foo_ptr(new goo));
foos.push_back(foo_ptr(new moo));
for(auto it = foos.begin(); it!=foos.end(); ++it) {
it->get()->print();
}
return 0;
}
Source: http://ideone.com/ym4SY2
foo
(that is, have a (smart) pointer infoo
, pointing to afoo_impl
object, that may be subclassed). Now externally,foo
appears to be a value object, but internally it may forward to different implementations at runtime with different run-time data. – Cryobiologymoo
, and madefoo
from it that is no longer amoo
. "Slicing" is almost always a bad thing. The vector is not storing objects of different sizes, it only storesfoo
objects, which are all the same size. – Trenatrenailfoo
objects, not pointers to objects derived fromfoo
. – Lovelornvector
can't instantiate with an abstract value type. The linked code says "error: cannot allocate an object of abstract type ‘foo’" – Trenatrenailfoo
whos members are a copy of thefoo
members of themoo
used to construct it, but thefoo
in the vector does not refer to the extra members in any way, shape, or form, becausevector
containsfoo
objects, notmoo
objects. If you call a virtual function, themoo::
versions are used. If there is nonmoo::
version, it can't even be compiled. – Trenatrenailstd::vector<short> v; v.push_back(1000000000);
. What happens? The vector holds one short, not one integer. – Trenatrenail