Taylor
Taylor

Reputation: 2085

Why doesn't the function cast a pointer correctly (from base class to derived class)

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

Answers (3)

user4581301
user4581301

Reputation: 33952

std::queue<std::shared_ptr<Base>> q;

is a queue of (shared) pointers to Bases, 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

alain
alain

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.

Working code online

Upvotes: 2

Usama Chaudhary
Usama Chaudhary

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

Related Questions