为什么函数没有正确地转换指针(从基类到派生类)

时间:2017-10-25 22:44:44

标签: c++ pointers shared-ptr

将函数强制转换为基类型的指针,指向派生类型的指针的最佳方法是什么?以下为什么不自动执行?

#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;
}

使用以下作品替换foo。但是,这可能不会强制参数成为Derived类,这是我在编写的程序中应该唯一的。

void foo(const std::shared_ptr<Base>& dp){
    std::cout << "it runs\n";
}

我也可以手动投射指针。还有其他想法吗?

修改 一些人建议我使用虚函数。我想他们的意思是我应该将foo放在类中作为虚函数,如下所示。但是,如果我错了,请纠正我的理解。

#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;
}

编辑2: 谢谢大家的帮助。目前我已经接受了答案,但在我的真实计划中,我可能会倾向于使用@ alain的演员答案。在这个程序中,我的队列中充满了一次一个弹出的事件。除了这个队列,我有多个事件处理程序 - 根据它的派生类的类型,我将使用不同的事件处理程序的函数。因此,考虑这些事件拥有事件处理程序功能对我来说没有多大意义。实际上这让我觉得我需要重新考虑在事件中继承。如果有一些队列允许不同类型的对象,我根本不会遇到这个问题。也许这有其他原因,但我不确定。

3 个答案:

答案 0 :(得分:2)

需要进行两项更改:

通过向virtual类添加Base函数来启用多态性。有一个虚拟析构函数是个好主意,所以

virtual ~Base() {};

然后,std::shared_ptr<Base>无法自动转换为std::shared_ptr<Derived>。您必须使用dynamic_pointer_cast

foo(dynamic_pointer_cast<Derived>(q.front())); // error

请同时检查是否真的需要这样做,因为不需要从基础到衍生的模型的设计通常会更好。

Working code online

答案 1 :(得分:2)

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

Base的(共享)指针队列,所以

q.front()

将返回std::shared_ptr<Base>

void foo(const std::shared_ptr<Derived>& dp)

需要std::shared_ptr<Derived>

虽然BaseDerived之间存在关联,但std::shared_ptr<Base>std::shared_ptr<Derived>之间没有任何关系。它们是两种不同类型的shared_ptr

“那么,”你可能会想,“我只会shared_ptr::get指针。赢了!”你可以这么想,但是......不。 DerivedBase,但Base不是Derived

此时您有两种选择:将Base投射到Derived,因为您知道它是Derived,但这只适用于您玩具代码中知道这是Derived。真正的代码变得更加混乱。这是一个坏主意。

所以,让我们跳过好主意,好吗?虚函数。

#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;
}

答案 2 :(得分:1)

您需要在Base类中使用虚拟函数,否则会将其视为继承而不是多态,因此它不会引用您的带有基类指针的子对象。