Why Doesn't Polymorphism Work Without Pointers/References

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.

Does polymorphism work in C++ without pointers / references? [duplicate]

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

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?

The reason why not able to use polymorphism with values but references and pointers

The problem with treating values polymorphically boils down to the object slicing issue: since derived objects could use more memory than their base class, declaring a value in the automatic storage (i.e. on the stack) leads to allocating memory only for the base, not for the derived object. Therefore, parts of the object that belong to the derived class may be sliced off. That is why C++ designers made a conscious decision to re-route virtual member-functions to the implementations in the base class, which cannot touch the data members of the derived class.

reference and pointer used for polymorphism? [duplicate]

Lets say you've 3 different cars. And you've different mechanisms of driving them. The driver needn't know about the underlying engines but only the protocol of how to drive a car i.e. press this pedal for acceleration, press that pedal for brakes, etc.

Now from the driver's perspective, it doesn't matter if its Honda, Ford or Buick. From his viewpoint, it is just a car. Likewise, if you've shed, where cars are parked, you call them a car shed. It houses cars and isn't bothered about what make each one is. So

std::vector<Car*> v;
v.push_back(new Ferrari());
v.push_back(new Honda());
v.push_back(new Ford());

why do we need to use reference / pointer when same work can done by creating object of class?

Without a pointer or reference, you cannot create a collection of objects that have some commonality, but different in some specific sense. This is circumvented in some strict OOP languages like Java, C#, etc. by making all objects derive from a base class called Object. C++ is a multi-paradigm language and the programmer is free to make such decisions as appropriate to his/her project. A way to do it in C++ is via pointers of the base class. This idiom is called run-time polymorphism.

for (const auto &this_car : v)
this_car->drive();

Here irrespective of the actual make, the vector v will be able to hold the car, as long as the base class car is part of the type. Likewise, drive will be different for each make, but that isn't required to be known for the function which calls it.

EDIT:

Thanks to nijansen for pointing out that this post doesn't actually answer the question: why pointers or references (dynamic types) are required for run-time polymorphism? it only says they've to used to achieve it but doesn't explain why can't we use ordinary (static type) variables.

C++ is designed in such a way that an object's type may or may not be known completely at compile-time with just the type in hand. In Circle c; we know c is of type Circle; whereas in Shape *p = make_shape(); we really do not know what object p is pointing to; p's own type is Shape* but its pointed-to object's concrete type is unknown. All we know is that it is pointing to some object which derives from Shape. Please do not confuse yourself with dynamic or automatic memory allocation; this is the same for automatic variables too E.g. Shape *p = &c;. Here too p in isolation the compiler doesn't know what concrete type the object is of, which p is pointing to.

Had we written p as a static (non-pointer, non-reference) type Shape p = make_shape(); or Shape p = c; what really happens in slicing i.e. p will be of the concrete type Shape and since it's not a pointer, it'll copy (Shape) part of Circle object c to it, which is mostly undesirable.

Without knowing the underlying type, how do we call the right concrete type's functions? C++ provides virtual functions to do the job. This is why runtime polymorphism is always explained with virtual methods in C++; while in languages like Java and C# all methods are virtual and all object types are references/pointers. In a way the answer to your question is, the language is designed such that one needs reference/pointer variables for runtime polymorphism.

Why does the compiler allow references to be used for run-time polymorphism in C++?

"The reference is fixed" premise is false. A reference may refer to the base subobject of any object in the hierarchy, just like the pointer can. The compiler cannot tell from the reference what the most-derived object is, no more than it can from the pointer.

Consider:

void DoSomething(const Base& b) { std::cout << b.getName(); }

Base base;
DoSomething(base); // b is bound to Base object. Prints "Base"

Derived derived;
DoSomething(derived); // b is bound to Base subobject of Derived object.
// Prints "Derived"

Why does dynamic cast only work with references and pointers

dynamic_cast has only one purpose: casting along an inheritance hierarchy, specifically up the hierarchy as well as down the hierarchy. Let's assume it worked for values as in your example, what will happen here:

bar b{};

auto sliced = dynamic_cast<foo>(b);

The object sliced is of type foo, but contains only the foo subobject of b. This is practically never what you want. Handling instances of polymorphic types by value is not a good option, see also this thread.

For the inverse direction, it is even more obvious why this can't really work well:

bar b{};
foo& f = b;

// Pass f by value to dynamic_cast?! Then, the argument is already
// sliced before the dynamic_cast is even able to determine its
// runtime type.
bar downcast = dynamic_cast<bar>(f);

The comment in the last example is a bit exaggerated, as dynamic_cast is an operator and not a function, but it should pinpoint the issue here: conversions across a hierarchy don't play nice with value semantics, and dynamic_cast reflects that.

Reference and pointer in polymorphism

Is the reference version better?

Yes, although a better way to put this would be "the pointer version is worse". The problem with the pointer version is that you pass it a valid pointer, and get a dangling pointer when the function returns. This is not intuitive, and leads to maintenance headaches when someone modifies your code thinking that you have forgotten to delete cs and ss in the main, not realizing that f deletes its argument.

The version that uses a reference is much better in this respect, because the resources are managed automatically for you. Readers of your code do not need to track the place where the memory of cs and ss gets released, because the allocation and release happen automatically.

I try to use unique_ptr, however, I get errors

There is no implicit conversion from std::unique_ptr<T> to T*. You need to call get() if you want to pass a raw pointer:

f(cs.get());
f(ss.get());

Are references and pointers equal with regards to polymorphism?

With regard to polymorphism, references work just like pointers.



Related Topics



Leave a reply



Submit