I'm new to C++ and have a question about member variable polymorphism. I have the following class definitions -
class Car
{
public:
Car();
virtual int getNumberOfDoors() { return 4; }
};
class ThreeDoorCar : public Car
{
public:
ThreeDoorCar();
int getNumberOfDoors() { return 3; }
};
class CarPrinter
{
public:
CarPrinter(const Car& car);
void printNumberOfDoors();
protected:
Car car_;
};
and implementation
#include "Car.h"
Car::Car()
{}
ThreeDoorCar::ThreeDoorCar()
{}
CarPrinter::CarPrinter(const Car& car)
: car_(car)
{}
void CarPrinter::printNumberOfDoors()
{
std::cout << car_.getNumberOfDoors() << std::endl;
}
The problem is when I run the following, the getNumberOfDoors of the parent class is called. I can get around this issue by making the member variable Car a pointer, but I prefer to pass in the input by reference instead of by pointer (which I understand to be preferred). Could you tell me what I'm doing wrong? Thanks!
ThreeDoorCar myThreeDoorCar;
std::cout << myThreeDoorCar.getNumberOfDoors() << std::endl;
CarPrinter carPrinter(myThreeDoorCar);
carPrinter.printNumberOfDoors();
By making a copy of the object you sacrifice its polymorphic abilities. Whatever type of car you pass, the copy will be of type Car (the base class), because that is what it is declared as.
If you want to keep using polymorphism either use a pointer or a reference. Here is the version using a reference:
class CarPrinter
{
public:
CarPrinter(const Car& car);
void printNumberOfDoors();
protected:
const Car &car_; // <<= Using a reference here
};
As you can see, this way you can continue using a constructor that takes a reference as argument. (These references don't have to be const, although const makes sense as long as the purpose of the CarPrinter is just printing.)
One potentially undesirable side-effect of this is that you can't change what the reference refers to after constructing the CarPrinter object. If you need to print the information for a different object, you'll have to create a new CarPrinter object for that. These objects would then really just act as (probably short-lived) wrappers around references.
If you don't like this, you can still continue passing a reference to the constructor, but turn it into a pointer by taking its address in the constructor implementation and then storing that.
When you do:
Car m_car;
It will not treat the m_car instance polymorphically, even if Car has subclasses and virtual functions. It will just use Car functions. This is called static binding - it determines which function to call at compile time based on the static type (Car) .
You need a reference or pointer for it to be handled polymorphically via dynamic dispatch by looking up the correct virtual function via the virtual function table of the instance's dynamic type (e.g. ThreeDoorCar or TwoDoorCar etc) at runtime. Polymorphic call behaviour is achieved through pointers or references in combination with virtual function declarations. This is more or less a direct result of syntactically using values vs pointers/refs (See #kfmfe04's comment below).
Car* pCar;
Car& rCar = x_car;
Virtual members called via a pointer or reference (e.g. pCar->getNumberOfDoors() or rCar.getNumberOfDoors()) does a vtable lookup at run time (dynamic dispatch). Because only at runtime does it know the dynamic type of the instance.
But m_car.getNumberOfDoors() is a virtual member that is called directly, and the compiler knows at compile time the direct (static) type and function address, statically binding the function address (Car::getNumberOfDoors) at compile time.
The problem is in this line of the CarPrinter constructor:
: car_(car)
This calls the compiler generated default copy constructor for the Car class, which ends up creating an instance of Car, not ThreeDoorCar.
Unfortunately, you'd need to pass the pointer, or pass by reference, but store the pointer. For example:
class CarPrinter
{
public:
CarPrinter(const Car& car)
:car_(&car) {};
...
protected:
const Car* car_;
};
Related
I have two functions whose signatures I can't change. The first one takes a reference to an object, while the second takes a universal reference to a unique pointer of the same object. I'm not sure how to pass the first argument to the second.
I've tried by passing a new unique ptr with the address of the reference, but because the reference is abstract, the compiler complains about the unimplemented methods.
Here is an example:
// Example program
#include <iostream>
#include <memory>
class MyAbstractClass {
public:
virtual void doSomething() = 0;
};
class MyConcreteClass : public MyAbstractClass {
public:
void doSomething() override {
std::cout << "Hello I do something" << std::endl;
}
};
class MyUserClass {
public:
MyUserClass(MyAbstractClass& mac){
// the line below doesn't work !!
doSomethingElse(std::make_unique<MyAbstractClass>(&mac));
}
void doSomethingElse(std::unique_ptr<MyAbstractClass>&& mac){
mac->doSomething();
}
};
int main() {
MyConcreteClass mcc;
MyUserClass muc(mcc);
}
Your compiler complains because make_unique calls new on the type you are trying to instantiate, effectively copying the existing object. Of course, it can't do that, as the class is abstract.
Unless you have a way to guarantee that the reference passed to MyUserClass is to a dynamic ("heap") variable (and its pointer is not owned, etc.), you cannot just capture its pointer in unique_ptr and then release it after doSomethingElse. Even if this is guaranteed, for all you know, doSomethingElse could still try to delete the pointer itself (unless you know exactly what it does). As you pass it by rvalue reference, the object may not as well be valid after doSomethingElse returns.
That means you have to make a copy of the object. You can't make a plain copy though. You need to do a copy of the entire, non-abstract, underlying object.
If you are allowed to change signature of the classes, adding a virtual clone method would to the trick.
If not, a not-absolutely-terrible workaround could be (you need to know beforehand all the types that inherit MyAbstractClass though) switch(typeid(mac)) and dynamic_cast.
I use a library that only returns references to created objects Entity& Create(int id). In my class, I need to create one of these and store it.
I had thought to use class member std::reference_wrapper<Entity> MyClass::m_Entity but the problem is, I would like to create this object in a call to a method like MyClass::InitEntity() – so I run into a compile error "no default constructor available" because m_Entity is not initialised in my constructor.
Is there any way around this, other than to change my class design? Or is this a case where using pointers would make more sense?
Is MyClass in a valid state if it doesn't have a valid reference to an Entity? If it is, then you should use a pointer. The constructor initializes the pointer to nullptr, and the InitEntity function assigns it to the address of a valid Entity object.
class MyClass
{
public:
MyClass(): _entity(nullptr) {}
void InitEntity() { _entity = &Create(123); }
void doSomethingWithEntity()
{
if (_entity) ...
}
private:
Entity *_entity;
};
If MyClass isn't in a valid state without a valid reference to an Entity, then you can use a std::reference_wrapper<Entity> and initialize it in the constructor.
class MyClass
{
public:
MyClass(): _entity(Create(123)) {}
void doSomethingWithEntity()
{
...
}
private:
std::reference_wrapper<Entity> _entity;
};
Of course which one you go with depends on how MyClass is supposed to be used. Personally, the interface for std::reference_wrapper is a little awkward for me, so I'd use a pointer in the second case as well (while still ensuring that it's always not null).
Let's say I have 2 classes Instrument and Brass, where Brass is derived from Instrument:
class Instrument
{
protected:
std::string _sound;
public:
Instrument(std::string sound) : _sound(sound) {}
virtual void play() { std::cout << _sound << std::endl; }
};
class Brass : public Instrument
{
private:
std::string _pitchShifter;
public:
Brass(std::string pitchShifter) : Instrument("braaaaaa"), _pitchShifter(pitchShifter)
{}
void printPitchShifter() { std::cout << _pitchShifter << std::endl; }
}
For some crazy reason I have a pointer to a member function of Instrument:
typedef void(Instrument::*instrumentVoidFunc)() //declaring member function pointers is too damn confusing
instrumentVoidFunc instrumentAction;
Now obviously, this will work, with well-defined behaviour:
Instrument soundMaker("bang!");
instrumentAction = &Instrument::play;
(soundMaker.*instrumentAction)();
And the output should be bang!.
But I can also make instrumentAction point to a Brass member function not present in Instrument by upcasting it, like so:
instrumentAction = static_cast<instrumentVoidFunc>(&Brass::printPitchShifter);
My understanding is (or was, anyway) that upcasting the member function pointer should destroy any ability for it to refer to derived class functions that aren't already present in the base class. However:
Brass trumpet("valves");
(trumpet.*instrumentAction)();
...prints out valves just as if I had called the function on the derived class normally. So apparently upcasting a derived class function pointer does not affect what happens when it is dereferenced on a derived class (though dereferencing it on a base class gives undefined behaviour).
How exactly does the compiler make this possible?
Although function pointer casts are possible, calling a function through a function type which doesn't match the original type is undefined behavior. The reason function pointer casts are allowed is to support casting to and storing a common type but recovering the correct by casting the function pointer back before calling it. The basic background for this restriction is that even compatible pointers may require adjustment upon use. Hiding the correct signature would imply a suitable trampoline to exist (i.e., the function pointer would need to have additional state).
I have a question about style. I have a class (in my case an Option) that depends on the value of an exogenous object (Interest Rate). My goal is to create a abstract base class for the exogenous object (Rate) so that I can construct variations, say SimulatedRate or ConstantRate, that will work inside my depending class, Option.
However, I'm finding in C++, since I obviously cannot instantiate a abstract base class, I must store either a pointer or a reference to the base class. My concern is that when the instantiated exogenous objects go out of scope outside of the dependent class, my dependent class will be pointing to junk.
Is there a reasonable way to utilize polymorphism for this problem in C++?
My current code:
class Dependent
{
public:
Dependent(const Exogenous& exo) : exo_(exo) {}
double getSomething() const { exo_.interfaceMethod(); }
private:
Exogenous& exo_;
}
class Exogenous
{
public:
virtual double interfaceMethod() const=0;
}
class ExogenousVariationA
{
public:
virtual double interfaceMethod() const { return resultA; }
}
class ExogenousVariationB
{
public:
virtual double interfaceMethod() const { return resultB; }
}
Your worry is valid. Since you are storing to a reference an object passed in by the client, you are trusting that client to keep the object alive while you need it. This can easily lead to problems. Of course, the same would be true if you used raw pointers to dynamically allocated objects. If the client does delete on the object before you're done with it, once again you have a problem.
The solution is to force the client to give you some kind of responsibility over the lifetime of the object. The way to do this is to ask for a smart pointer. Depending on your problem, you may want a std::unique_ptr or std::shared_ptr. Use the former if you want to take ownership from the client or the latter if you want to share ownership with them. Let's say you choose std::unique_ptr, you would then define your Dependent class as:
class Dependent
{
public:
Dependent(std::unique_ptr<Exogenous> exo) : exo_(std::move(exo)) {}
double getSomething() const { exo_->interfaceMethod(); }
private:
std::unique_ptr<Exogenous> exo_;
}
The client would use this like so:
std::unique_ptr<Exogenous> ptr(new ExogenousVariationA());
Dependent dep(std::move(ptr));
Now, when your client passes the std::unique_ptr to you, they're giving you ownership of the object. The object will only be destroyed when your std::unique_ptr is destroyed (which will be when your Dependent is destroyed, since it is a member).
Alternatively, if you take a std::shared_ptr then the object will be destroyed once both the client's and your std::shared_ptrs are destroyed.
sftrabbit has some good advice, to which I'd add:
you could create a virtual clone() method in the abstract base class (it's not a virtual base class - that's something else entirely); that method would be implemented in the derived interest rate classes, returning a pointer to a new independent interest rate object that can be owned by the Option; this is particularly useful if the objects contain data that changes as you use it (e.g. from calculations or caching)
you probably don't want this, but with std/boost shared pointers it's also possible to ask for a weak pointer to the shared object... that way you can test whether the "owners" of the object (which won't include you) have already finished with it and triggered its destruction
Separately, to use runtime polymorphism your ExogenousVariationA and ~B classes must actually derive from Exogenous, and the method you want to be polymorphically dispatched must be virtual. That looks like this:
class Exogenous
{
public:
virtual double interfaceMethod() const=0;
}
class ExogenousVariationA : public Exogenous
{
public:
double interfaceMethod() const { return resultA; }
}
This surprised me a little bit, but I was playing around with some code and found out that, at least on my computer, when a function accepts a parent class by reference and you pass a child instance, that the slicing problem doesn't occur. To illustrate:
#include <iostream>
class Parent
{
public:
virtual void doSomething()
{
using namespace std;
cout << "Parent::DoSomething" << endl;
}
};
class Child : public Parent
{
public:
virtual void doSomething()
{
using namespace std;
cout << "Child::DoSomething" << endl;
}
};
void performSomething(Parent& parent)
{
parent.doSomething();
}
int main(int argc, char** argv)
{
Child myChild;
performSomething(myChild);
return 0;
}
This prints out Child::DoSomething.
Like I said, I was a little surprised. I mean, I know that passing by reference is like passing pointers around (but much safer in my understanding), but I didn't know I still got to keep polymorphic goodness when doing so.
I just want to make sure, is this supposed to happen or is it one of those "it works on my machine" type of instances?
"Slicing" refers to the inability of the base copy constructor to distinguish exact type matches from derived classes. The only way to invoke slicing is to invoke the base copy constructor. Typically this occurs when passing arguments by value, though other situations can be contrived:
class Base { };
class Derived : public Base { };
void foo(Base);
int main()
{
Derived x;
Base y = x; // flagrant slicing
foo(x); // slicing by passing by value
}
You're never doing any such thing, so you do not encounter any slicing situations.
The behavior you are seeing is correct. This is how it is supposed to work. References work just like pointers.
That's supposed to happen. Passing by reference is EXACTLY like passing pointers -- it's doing the same thing under the hood. There's no magic to this; every instance of a polymorphic object has a virtual function table associated with it. As long as you don't copy anything, you're not going to lose that information and your virtual function calls will work the way you expect they would.
The reason that you run into problems when passing by value is that it will use the copy constructor of the type you specified in the function signature, so you end up with an entirely new instance of the superclass.
Yes, binding to a reference enables dynamic binding. This is caused by the difference of the dynamic type of an object and its static type.
If you take your parameter by value, it becomes a Parent class. Although if you pass something through a reference or a pointer and call a virtual function, the run-time will look for the dynamic type or most-derived type of the actual object that is being referenced.