C++ polymorphism without pointers
Ultimately, no.
Polymorphism only works with non-value types: references and pointers. And since references can only be bound once, you cannot really use them in standard containers. That leaves you with pointers.
You're attacking the problem at the wrong end. If you are concerned about the overhead of allocating lots of small objects (and I'm assuming that this is a legitimate concern. That is, you have actual profiling data or sufficient experience to know it is a concern for your specific application), then you should fix that. Change how you're allocating memory for these objects. Make a small allocation heap or something.
Admittedly, pre-C++0x's allocators are somewhat lacking in this regard, since they have to be stateless. But for your purposes, you should be able to deal with it.
From your edit:
That is a terrible idea. Erasing from a std::deque
at anywhere but the start or end will invalidate every pointer in your std::list
.
Given your comment, this idea is functional. However, having all of these different memory blocks for different kinds of objects seems to go against the whole point of inheritance. After all, you can't just write a new type of Animal
and slip it into the std::list
; you have to provide memory management for it.
Are you sure that inheritance-based polymorphism is what you need here? Are you sure that some other methodology would not work just as well?
Does polymorphism work in C++ without pointers / references?
No it will not be possible without pointers.
Since you create an object B
and push it to the vector containing A
, it will get copied (sent to the copy constructor of A
) and an instance of A
will be added to the vector. I.e. the object will be sliced.
Given this code:
struct A {
virtual void d() {
std::cout << "Hello A" << std::endl;
}
};
struct B : public A {
virtual void d() {
std::cout << "Hello B" << std::endl;
}
};
int main() {
std::vector<A> v;
v.push_back(B());
v[0].d();
}
The output will be:
Hello A
Why doesn't polymorphism work without pointers/references?
In C++, an object always has a fixed type and size known at compile-time and (if it can and does have its address taken) always exists at a fixed address for the duration of its lifetime. These are features inherited from C which help make both languages suitable for low-level systems programming. (All of this is subject to the as-if, rule, though: a conforming compiler is free to do whatever it pleases with code as long as it can be proven to have no detectable effect on any behavior of a conforming program that is guaranteed by the standard.)
A virtual
function in C++ is defined (more or less, no need for extreme language lawyering) as executing based on the run-time type of an object; when called directly on an object this will always be the compile-time type of the object, so there is no polymorphism when a virtual
function is called this way.
Note that this didn't necessarily have to be the case: object types with virtual
functions are usually implemented in C++ with a per-object pointer to a table of virtual
functions which is unique to each type. If so inclined, a compiler for some hypothetical variant of C++ could implement assignment on objects (such as Base b; b = Derived()
) as copying both the contents of the object and the virtual
table pointer along with it, which would easily work if both Base
and Derived
were the same size. In the case that the two were not the same size, the compiler could even insert code that pauses the program for an arbitrary amount of time in order to rearrange memory in the program and update all possible references to that memory in a way that could be proven to have no detectable effect on the semantics of the program, terminating the program if no such rearrangement could be found: this would be very inefficient, though, and could not be guaranteed to ever halt, obviously not desirable features for an assignment operator to have.
So in lieu of the above, polymorphism in C++ is accomplished by allowing references and pointers to objects to reference and point to objects of their declared compile-time types and any subtypes thereof. When a virtual
function is called through a reference or pointer, and the compiler cannot prove that the object referenced or pointed to is of a run-time type with a specific known implementation of that virtual
function, the compiler inserts code which looks up the correct virtual
function to call a run-time. It did not have to be this way, either: references and pointers could have been defined as being non-polymorphic (disallowing them to reference or point to subtypes of their declared types) and forcing the programmer to come up with alternative ways of implementing polymorphism. The latter is clearly possible since it's done all the time in C, but at that point there's not much reason to have a new language at all.
In sum, the semantics of C++ are designed in such a way to allow the high-level abstraction and encapsulation of object-oriented polymorphism while still retaining features (like low-level access and explicit management of memory) which allow it to be suitable for low-level development. You could easily design a language that had some other semantics, but it would not be C++ and would have different benefits and drawbacks.
C++ std containers - polymorphism without pointers. Is it possible?
Yes, you do have to use pointers. Otherwise, attempting to put a B
into a container of A
results in slicing: the B
gets cut down into an A
(this is not limited to containers, the exact same thing happens if you do A a = B()
or if you pass a B
to a function expecting an A
).
When you later take it back out, it's an A
that has absolutely no knowledge its lineage includes an illustrious forefather of type B
-- and no matter what way you look at an A
, you can't make it a B
.
polymorphism: calling overrided functions without pointers
When you placed Ferrari in your first list you experienced type erasure - the "GenericCar" structure was copied into the list and anything that could have identified that it was a "FerrariCar" was lost.
You need a pointer or reference to invoke polymorphic functions, have a pointer or reference gives you access to the virtual table for your object.
To have a list that could store store such car objects and be passed around to different functions you will probably want to use smart pointers so that you don't wind up with dangling pointers or memory leaks.
#include <memory>
...
list<shared_ptr<GenericCar>> cars;
cars.push_back(shared_ptr<GenericCar>(new GenericCar()));
cars.push_back(shared_ptr<GenericCar>(new FerrariCar()));
for ( shared_ptr<GenericCar> & car : cars )
car->PrintModelName();
Why we can't implement polymorphism in C++ without base class pointer or reference?
You are asking a question and providing a code example that fails but for a different reason. From the wording of your question:
Why are references/pointers required for polymorphism?
struct base {
virtual void f();
};
struct derived : public base {
virtual void f();
};
void call1( base b ) {
b.f(); // base::f
}
void call2( base &b ) {
b.f(); // derived::f
}
int main() {
derived d;
call1(d);
call2(d);
}
When you use pass-by-value semantics (or store derived elements in a base container) you are creating copies of type base
of the elements of type derived
. That is called slicing, as it resembles the fact that you have a derived
object and you slice/cut only the base
subobject from it. In the example, call1
does not work from the object d
in main, but rather with a temporary of type base
, and base::f
is called.
In the call2
method you are passing a reference to a base
object. When the compiler sees call2(d)
in main it will create a reference to the base
subobject in d
and pass it to the function. The function performs the operation on a reference of type base
that points to an object of type derived
, and will call derived::f
. The same happens with pointers, when you get a base *
into a derived
object, the object is still derived
.
Why can I not pass a container of derived
pointers to a function taking a container of base
pointers?
_Clearly if derived
are base
, a container of derived
is a container of base
.
No. Containers of derived
are not containers of base
. That would break the type system. The simplest example of using a container of derived
as container of base
objects breaking the type system is below.
void f( std::vector<base*> & v )
{
v.push_back( new base );
v.push_back( new another_derived );
}
int main() {
std::vector<derived*> v;
f( v ); // error!!!
}
If the line marked with error was allowed by the language, then it would allow the application to insert elements that are not of type derived*
into the container, and that would mean lots of trouble...
But the question was about containers of value types...
When you have containers of value types, the elements get copied into the container. Inserting an element of type derived
into a container of type base
will make a copy of the subobject of type base
within the derived
object. That is the same slicing than above. Besides that being a language restriction, it is there for a good reason, when you have a container of base
objects, you have space to hold just base
elements. You cannot store bigger objects into the same container. Else the compiler would not even know how much space to reserve for each element (what if we later extend with an even-bigger type?).
In other languages it may seem as this is actually allowed (Java), but it is not. The only change is in the syntax. When you have String array[]
in Java you are actually writting the equivalent of string *array[]
in C++. All non-primitive types are references in the language, and the fact that you do not add the *
in the syntax does not mean that the container holds instances of String, containers hold references into Strings, that are more related to c++ pointers than c++ references.
Polymorphism without new
In function f
objects B()
or C()
are both temporary, so you can only return them from f
by value.
Maybe boost::variant is for you. Then you don't even need to have the method virtual or derive from a common base class.
Related Topics
Why Is Copy Constructor Called Instead of Conversion Constructor
C++ Convert Integer to String at Compile Time
How to Check If the Program Is Run from a Console
Compiling Cuda Code in Qt Creator on Windows
Best Way for Interprocess Communication in C++
Portable Unused Parameter MACro Used on Function Signature for C and C++
How to Run Application Which Requires Admin Rights from One That Doesn't Have Them
Convert Float to Std::String in C++
Arduino Sprintf Float Not Formatting
Correct Usage(S) of Const_Cast<>
What Std::Locale Names Are Available on Common Windows Compilers
Inserting into an Unordered_Set with Custom Hash Function
How to Automatically Avoiding Stepping into Certain Functions in Visual Studio