How to Pass Derived Classes by Reference to a Function Taking Base Class as a Parameter

Is it possible to pass derived classes by reference to a function taking base class as a parameter

Yes.
You don't have to upcast your objects. All references/pointers to derived types are converted implicitly to base objects references/pointers when necessary.

So:

IBase* ptr = new CFoo("abc"); // good
CFoo* ptr2 = static_cast<CFoo*>(ptr); // good
CFoo* ptr3 = ptr; // compile error

CFoo instance("abc");
IBase& ref = instance; // good
CFoo& ref2 = static_cast<CFoo&>(ref); // good
CFoo& ref3 = ref; // compile error

When you have to downcast you may want to consider using dynamic_cast, if your types are polymorphic.

Passing derived class to a function of base class argument

A function taking a reference or pointer refers to the original object passed in, while by-value arguments will create a copy of your object. Since you are only copying the base part (since it takes a base object), you end up working with a copy of just the base part, and it acts like a base because it is a base.

This "base-only" copying is called "slicing" because it only copies part of your object, "slicing off" the derived part.

how to pass a derived class as a function argument: what if the derived class members are not in the parent class

Jail.

You can do it using shared_ptr, dynamic_pointer_cast and static_cast, as i show you in the above code.

BTW, be careful with the design of your class because it could be affected at the time of derived it and casting, as you can see in the second case of the class DerivedB where the address of the variable derived_member is taken by the variable new_member.

#include<memory>
#include<iostream>

class Base {};

class DerivedA :public Base {

public:
int derived_member;
};

class DerivedB :public Base {

public:
int new_member;
int derived_member;
};

void func(const std::shared_ptr<Base> &bp) {

DerivedA* a = static_cast<DerivedA*>(bp.get()) ;
std::cout << " VALUE MEMBER A: " << a->derived_member << std::endl;

}

int main(int)
{

std::shared_ptr<DerivedA> bpA = std::make_shared<DerivedA>();
std::shared_ptr<DerivedB> bpB = std::make_shared<DerivedB>();

bpA->derived_member = 666;
bpB->new_member = 0;
bpB->derived_member = 667;

std::shared_ptr<Base> pa = std::dynamic_pointer_cast<Base>(bpA);
std::shared_ptr<Base> pb = std::dynamic_pointer_cast<Base>(bpB);

func(pa);
func(pb);

return 0;
}

The output:

 VALUE MEMBER A: 666
VALUE MEMBER A: 0

But, if you are looking for a solution which implies to call derived_member without doing the cast to a Derived class, it is not possible because the class Base does not have the variable derived_member in its definition.

Passing derived class by reference to function taking base class as parameter

This is known as slicing.

When passing A by value, a local copy is made, by copying just the A part of the caller's object. Since this has both a static and dynamic type of A, that's the override of print that's chosen.

Passing by reference, the function gets a reference to the caller's object with dynamic type B, so that override is chosen.

Using abstract base classes can prevent confusing behaviour like this - they can't be directly instantiated, and so can't be passed by value.

How to pass derived class as a parameter to a method that asks for a base class

As long as your PhysicsObject does not implement the method TakeDamage (and reading your class names it never should) you can do the following:

void ProjectileHit(PhysicsObject p, PhysicsObject hit)
{
(hit as Enemy)?.TakeDamage(CurrentPlayer.EquippedTool.Damage);
}

'as' tries to cast hit to Enemy and returns null if hit is null or the cast fails.

'?' executes the code on its right only if the left side is not null.

Passing derived class to base function

Instead of a complete code review (-- which is not what SO is for), let's get directly to the routine you mentioned

void Board::setvalue(int length, int width, Obstacle& obstacle)
{
this->playfield[length][width] = &obstacle;
return;
}

which sets a triple pointer

Obstacle *** playfield;

This design is bad for several reasons, but here is the main one: it is not clear at all that the ostacle is still alive when you want to call it via Board::playfield. Nobody ensures that player isn't long destroyed, and you will be having a hard time in bookkepping this fact.

Instead, I suggest you to let the board own the obstacles. Thus, instead of an obstacle raw pointer, set up a vector of unique-pointers,

std::vector<std::unique<Obstacle> > playfield;

and then either copy or move the classes:

template<typename O>
void Board::setvalue(int length, int width, O&& obstacle)
{
playfield.push_back(std::make_unique<O>(std::forward<O>(obstacle));
}

(I've left the field geometry aside, I doubt that it is useful to intermix it with the actual storage of the obstacles -- but if you still want to you can use a vector of vectors or a single vector with a two-dimensional index scheme).

And here back to your intention: With the above approach, you directly get rid of all constness problems. You aka. the Board owns the stuff and can do with it what you want.

Why should you pass a reference instead of a value when passing a derived class object to a function with a base class parameter in C++?

The reason is that sizeof(A) and sizeof(B) are not the same. If a function (in C++ or the like) takes a parameter by value, it must know how large the value for that parameter will be in order to interpret the values in memory correctly. As a rough (but technically sketchy) example, suppose I have a function that takes an A and an int. Maybe it expects the incoming values to be stored like

AAAAAAiiii

where the first 6 bytes are the A object, and the last 4 bytes are the integer value. But I create a B, which looks something like AAAAAABBBBB... so now the function receives

AAAAAABBBBBiiii

and that's no good. Passing either a pointer or a reference allows the function to know how many of the bytes its receiving represent that first parameter.

So why isn't this a thing in Java? In java objects are always handled "by reference"; that is, when you say

Fruit apple = new Apple();

you're creating a variable apple which is a reference to a Fruit. If a java method says

public void iHaveAPenIHaveA(Fruit fruit)

it's going to accept a reference to a fruit.

In other words, what your Java sample is doing actually is the same thing as the by-reference version of your C++ sample (the one with void neverGonna(A& a)).



Related Topics



Leave a reply



Submit