Reputation: 2085
What's the best way to have a function cast a pointer to a base type, to a pointer to derived type? Why doesn't the following work automatically?
#include <iostream>
#include <memory>
#include <queue>
class Base{
public:
Base() {};
};
class Derived : public Base{
public:
Derived() : Base() {};
};
void foo(const std::shared_ptr<Derived>& dp){ // "wrong" signature
std::cout << "it runs\n";
}
int main(int argc, char **argv)
{
std::queue<std::shared_ptr<Base>> q;
q.push( std::make_shared<Derived>() );
foo(q.front()); // error
return 0;
}
Replacing foo
with the following works. However this might not enforce the argument to be a Derived class, which is the only thing it should be in the program I am writing.
void foo(const std::shared_ptr<Base>& dp){
std::cout << "it runs\n";
}
I could probably also cast the pointer manually. Any other ideas?
Edit:
a few people have suggested that I use virtual functions. I imagine that they mean I should put foo
inside the classes as a virtual function like the following. However, if I am wrong, then please correct my understanding.
#include <iostream>
#include <memory>
#include <queue>
class Base{
public:
Base() {};
virtual void foo () { std:: cout << "base\n"; };
};
class Derived : public Base{
public:
Derived() : Base() {};
void foo() {std::cout << "derived\n";};
};
int main(int argc, char **argv)
{
std::queue<std::shared_ptr<Base>> q;
q.push( std::make_shared<Derived>() );
q.front()->foo();
return 0;
}
Edit 2: Thanks, everyone, for the help. At the moment I've accepted an answer, but in my real program I will probably lean toward using @alain's casting answer, actually. In this program, my queue is full of events that I pop off one at a time. In addition to this queue, I have multiple event-handlers--depending on what type of derived class it is, I will use a different event handler's function. So it doesn't make much sense to me to think about these event's possessing the event-handlers function. Actually this makes me think I need to reconsider having inheritance in the events altogether. I wouldn't have this issue at all if there was some queue that would allow objects of different type. Maybe that's bad for other reasons, though, I'm not sure.
Upvotes: 0
Views: 80
Reputation: 33952
std::queue<std::shared_ptr<Base>> q;
is a queue of (shared) pointers to Base
s, so
q.front()
will return a std::shared_ptr<Base>
.
void foo(const std::shared_ptr<Derived>& dp)
Requires a std::shared_ptr<Derived>
While there is a relationship between Base
and Derived
, there is no relationship between std::shared_ptr<Base>
and std::shared_ptr<Derived>
. They are two different types of shared_ptr
.
"Well then," you might think, "I will just shared_ptr::get
the pointer out. Winning!" You can think that, but... no. Derived
is a Base
, but Base
is not a Derived
.
At this point you have two options: cast Base
to a Derived
since you know dang well that it is a Derived
, but this is only really viable in toy code where you DO know it's a Derived
. Real code gets messier. It's a bad idea.
So lets just skip to the good idea, shall we? Virtual Functions.
#include <iostream>
#include <memory>
#include <queue>
class Base
{
public:
// none of the constructors do anything, so I got rid of them.
// code that's not there has no bugs.
virtual ~Base() = default; // must provide virtual destructor so that
// correct destructors get called when a Base
// is destroyed
virtual void foo() = 0; // pure virtual function. All descendants must
// provide this function
// Pure virtual has the side effect of making
// it impossible to instantiate a Base.
// This may or may not be what you want. If it
// isn't, remove the =0 and implement the function.
/*
virtual void foo()
{
std::cout << "I pity the foo who derives from Base.\n"
}
*/
};
class Derived: public Base
{
public:
void foo() // implement Base::foo
{
std::cout << "it runs\n";
}
};
int main()
{
std::queue<std::shared_ptr<Base>> q;
q.push(std::make_shared<Derived>());
q.front()->foo();
return 0;
}
Upvotes: 2
Reputation: 12047
Two changes are needed:
Enabling polymorphism by adding a virtual
function to the Base
class. It is a good idea to have a virtual destructor, so
virtual ~Base() {};
Then, a std::shared_ptr<Base>
can't be converted to std::shared_ptr<Derived>
automatically. You have to use a dynamic_pointer_cast
:
foo(dynamic_pointer_cast<Derived>(q.front())); // error
Please also check if this is really needed, because designs not requiring casts from base to derived are usually better.
Upvotes: 2
Reputation: 41
You need to have a Virtual Function in your Base class otherwise it will treat it as Inheritance not Polymorphism so it will not refer your child object with base class pointer.
Upvotes: 1