代码之家  ›  专栏  ›  技术社区  ›  Taylor

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

  •  0
  • Taylor  · 技术社区  · 8 年前

    让函数将指针转换为基类型或派生类型的最佳方法是什么?为什么下面的程序不能自动运行?

    #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 进行以下工作。然而,这可能不会强制参数为派生类,这是我正在编写的程序中唯一应该包含的内容。

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

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

    编辑: 一些人建议我使用虚拟函数。我想他们的意思是我应该 在类内部作为虚拟函数,如下所示。但是,如果我错了,请纠正我的理解。

    #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 回复  |  直到 8 年前
        1
  •  2
  •   alain    8 年前
    std::queue<std::shared_ptr<Base>> q;
    

    是指向的(共享)指针队列 Base s、 所以

    q.front()
    

    将返回一个 std::shared_ptr<Base> .

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

    需要 std::shared_ptr<Derived>

    虽然两者之间存在关系 基础 Derived ,两者之间没有关系 标准::shared\u ptr<基本(&T); 标准::shared\u ptr<导出(>); . 它们是两种不同类型的 shared_ptr

    “那么,”你可能会想,“我会 shared_ptr::get 指针伸出。获胜!“你可以这么想,但是……不。 衍生 是一个 基础 但是 基础 不是一个 .

    在这一点上,你有两个选择:演员 基础 到a 衍生 既然你很清楚这是一个 ,但这只有在你知道它是一个 衍生

    那么让我们跳到好主意,好吗?虚拟功能。

    #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
  •  2
  •   alain    8 年前

    需要进行两项更改:

    通过添加 virtual 功能 Base 班有一个虚拟析构函数是个好主意,所以

    virtual ~Base() {};
    

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

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

    还请检查是否真的需要这样做,因为不需要从基础到派生的转换的设计通常更好。

    Working code online

        3
  •  1
  •   Usama Chaudhary    8 年前

    虚拟函数 在基类中,否则它会将其视为 因此,它不会用基类指针引用子对象。