Polymorphic C++ References

polymorphic C++ references

There's nothing odd. Polymorphisms works both for pointers and references:

struct Base { };
struct Derived : Base;

void foo(Base &);

int main() {
Derived x;
foo(x); // fine
}

You're conflating this with another issue, namely creating a reference to a dynamic object:

T * pt = new T;
T & rt = *pt;

T & x = *new T; // same effect

Note that it's generally very bad style to track a dynamic object only by reference, because the only way to delete it is via delete &x;, and it's very hard to see that x needs cleaning up.

There are two immediate alternatives for your design: 1) make a a member object in B, or 2) make a a shared_ptr<A> or unique_ptr<A> and change the initalizer to a(new A1). It all depends on whether you actually need the polymorphic behaviour, i.e. if you have other constructors for B which assign a different derived class to a other than A1.

Are references and pointers equal with regards to polymorphism?

With regard to polymorphism, references work just like pointers.

polymorphism with reference member variable

As mentioned by others, you cannot reassign a reference.

Whenever you do something like bar = newfoo you are not resetting the reference. Instead you are invoking operator= for bar with newfoo as an argument.

Therefore, in your case you are slicing your objects and (let me say) copying its base part in bar.


A kind of reference-like tool to which you can reassign exists in the standard template library and it's called std::reference_wrapper.

It follows an example based on your code that uses it and has the expected behavior:

#include<functional>
#include <iostream>
#include <string>

class base
{
public:
virtual void print() { std::cout<<"base"<<std::endl;}
};
class derived: public base
{
public:
virtual void print(){ std::cout<<"derived"<<std::endl;}
};

class derived2: public base
{
virtual void print(){ std::cout<<"derived2"<<std::endl;}
};

class foo
{
public:
std::reference_wrapper<base> bar;
base boo;
derived foobar;
derived2 foobar2;
foo(): bar(boo){}
void newfoo(base & newfoo){ bar = newfoo; bar.get().print();}
};
int main()
{
foo test;
test.bar.get().print();
test.newfoo(test.foobar2);
}

In this case, operator= actually rebinds the reference to the given object. Anyway, as you can see, in this case you must invoke get to access the underlying reference.

Note: set aside the example above, your code isn't the typical use case for a std::reference_wrapper.

I mentioned it only for the sake of completeness.

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());

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"

reference and pointer used for polymorphism?

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.

Polymorphic r-value references?

std::move creates an rvalue reference to an existing lvalue, which marks it explicitly as ready to be moved from. You can initialize another rvalue reference from that, however you are not in the special lifetime-extension case for two reasons:

  • It requires an actual rvalue (i.e. an object that was just constructed within the expression and never passed through a reference);

  • More importantly, it does not apply to reference members at all.

Note: I'm reasonably sure that I've got this right, but I cannot seem to find a standard quote, probably because I'm getting lost in the aggregate initialization and temporary materialization wording. Language lawyers welcome.

Therefore you're just storing a reference to the constructor's parameter, which dies at the end of said constructor and leavs the reference dangling.

Note that when storing an arbitrary type-erased object as a member you cannot avoid dynamic allocation in the general case, if only because the dynamic type's size is unknown and unbounded. std::unique_ptr is the first choice for this case, but you could also design your own type-erasing container with small object storage if you wanted.

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.

c++ polymorphism - passing a reference to base class to a function

#include<iostream>
using namespace std;

class Shape {
public:
virtual float getArea() const = 0;
};

class Rectangle : public Shape {
private:
float width;
float height;

public:
Rectangle(float width, float height) : width(width), height(height) {}
float getArea() const {return width * height;}

};

class Circle : public Shape {
private:
float radius;

public:
Circle(float radius) : radius(radius) {}
float getArea() const {return 3.14159f * radius *radius;}
};

void printArea(const Shape& shape) {
std::cout << "Area: " << shape.getArea() << std::endl;
}

int main() {

Rectangle r(2,6);
Shape* shape = &r; // shape stores the address of object r which when further call
std::cout<< shape <<endl; //called sends its address
Circle c(6);
shape = &c; // same as r is being processed
std::cout<<shape<<endl;
printArea(r); // same thing is taking place here reference is passed and then polymorphism come
printArea(c); // into play
printArea(*shape); // as the shape was given reference of c in the end it returns its value

return 0;

}

if u still have any questions feel free to ask in comments!

Static polymorphism with forwarding references

If the goal is just to limit the use of polymorphicCall to types derived from Base you can do that with a static_assert and a type trait.

#include <iostream>

template <typename Derived>
struct Base
{
decltype(auto) foo() { return static_cast<Derived&>(*this).foo(); }
};

struct Derived : Base<Derived>
{
void foo() { std::cout << "Derived" << std::endl; }
};

template <typename T, typename = void>
struct IsDerivedFromBase : std::false_type {};

template <typename T>
struct IsDerivedFromBase<T, std::enable_if_t<std::is_base_of_v<Base<T>, T>>> : std::true_type {};

template <typename T>
struct Object
{
template <typename U>
Object(U&& data) : m_data(std::forward<U>(data)) {}

T m_data;
};

template <typename T>
decltype(auto) polymorphicCall(T&& obj)
{
using Derived = std::remove_cvref_t<T>;
static_assert(IsDerivedFromBase<Derived>::value);
return Object<Derived>(std::forward<T>(obj));
}

int main()
{
Derived d;
polymorphicCall(d);
int i;
//polymorphicCall(i);

return 0;
}


Related Topics



Leave a reply



Submit