C++ 对于重载函数,调用父实例和子实例的专用版本

C++ 对于重载函数,调用父实例和子实例的专用版本,c++,inheritance,polymorphism,overloading,C++,Inheritance,Polymorphism,Overloading,我之前问过一个问题,但结果证明我的问题并没有按照我的示例正确建模。这就是我的实际问题: 我有类A,类B继承自A 我有两个函数foo(A&)和foo(B&) 我有一个a*指针列表,其中包含a和B的实例 对于A的实例,如何调用foo(A&);对于B的实例,如何调用foo(B&)?约束条件:我可以修改A和B实现,但不能修改foo的实现 请参见下面的示例: #include <iostream> #include <list> class A { public: }; cla

我之前问过一个问题,但结果证明我的问题并没有按照我的示例正确建模。这就是我的实际问题:

  • 我有类
    A
    ,类
    B
    继承自
    A
  • 我有两个函数
    foo(A&)
    foo(B&)
  • 我有一个
    a*
    指针列表,其中包含
    a
    B
    的实例
  • 对于
    A
    的实例,如何调用
    foo(A&)
    ;对于
    B
    的实例,如何调用
    foo(B&)
    ?约束条件:我可以修改
    A
    B
    实现,但不能修改
    foo
    的实现
  • 请参见下面的示例:

    #include <iostream>
    #include <list>
    
    class A {
    public:
    };
    
    class B : public A {
    public:
    };
    
    void bar(A &a) { std::cout << "This is an A" << std::endl; }
    void bar(B &b) { std::cout << "This is a B" << std::endl; }
    
    int main(int argc, char **argv) {
      std::list<A *> l;
      l.push_back(new B());
      l.push_back(new B());
      for (std::list<A *>::iterator it = l.begin(); it != l.end(); ++it)
        bar(**it);
    }
    
    我期待着

    This is a B
    
    将指针传递到
    (通过重写其签名)没有帮助


    感谢Antonio帮助澄清问题。

    您正在寻找运行时多态性。虚拟成员方法“自然”支持这一点

    另一种方法是使用RTTI和
    A*
    B*
    并在成功后调用
    bar
    。。。或者
    static\u cast
    如果您确实确定存在
    B*
    对象。通常需要向下转换表示设计有问题

    重要注意事项:运行时签入要求类型无论如何都是多态的。也许您特定的
    A
    可以实现这一点,但您无法更改类。否则,
    static\u cast
    是唯一可用的选项

    如果您可以控制类,则可以使用标准多态性和重载机制,使用
    this
    上的虚拟方法作为“外部”调用的门面:

    #包括
    #包括
    甲级;
    无效外螺纹(A&);
    甲级{
    公众:
    虚拟空栏(){external_栏(*this);};
    };
    乙级;;
    空心外螺纹杆(B&)//重要的
    B类:公共A{
    公众:
    虚拟空栏(){external_栏(*this);};
    };
    
    void external_-bar(A&A){std::cout编辑:这回答了问题的第一个版本,现在改为查看


    如果您这样做:

    A* b = B();
    
    然后,
    *b
    将是A型。这就是你在for周期中所做的。这不涉及“虚拟性”或政治主义

    以下代码给出了您要查找的行为:

    class A {
    public:
    virtual void bar() { std::cout << "This is an A" << std::endl; }
    };
    
    class B : public A {
    public:
    virtual void bar() { std::cout << "This is a B" << std::endl; }
    };
    
    
    
    int main(int argc, char **argv) {
      std::list<A *> l;
      l.push_back(new B());
      l.push_back(new B());
      l.push_back(new A());
      l.push_back(new B());
      for (std::list<A *>::iterator it = l.begin(); it != l.end(); ++it)
        (*it)->bar();
    }
    

    将打印
    这是一个b

    其他人已经解释了如何实现它

    我只想解释一下为什么会这样

    B在这里隐式转换为A。因此,它当前只有A的属性

    上向铸造在C++中是隐含的。 <> Downcasting在C++中只有当基类是多态的时候才是可能的。 简言之,多态性需求只不过是基类中可以被派生方法覆盖的东西!!虚拟方法

    然后,您可以按照其他人的规定使用RTTI和dynamic_cast来实现这一点

    例如:

    #包括
    #包括
    甲级{
    公众:
    虚空虚拟对象()=0;
    };
    B类:公共A{
    公众:
    void dummy(){}
    };
    
    void bar(A&A){std::coutAntonio编写了一个涉及虚拟函数的好解决方案。如果出于某种原因您确实不想使用虚拟函数,那么您可以在自由函数中使用
    dynamic_cast

    #include <iostream>
    #include <list>
    
    struct A {
        virtual ~A() {}   // important
    };
    
    struct B : A {};
    
    void bar(A &a) { std::cout << "This is an A" << std::endl; }
    void bar(B &b) { std::cout << "This is a B" << std::endl; }
    
    void bar_helper(A *ptr)
    {
        if ( auto b = dynamic_cast<B *>(ptr) )
            bar(*b);
        else
            bar(*ptr);
    }
    
    int main()
    {
        std::list<A *> ls;
        ls.push_back(new B);
        ls.push_back(new B);
        ls.push_back(new A);
    
        for (auto ptr : ls)
        {
            bar_helper(ptr);
            delete ptr;
        }
        ls.clear();
    }
    
    #包括
    #包括
    结构A{
    虚拟~A(){}//重要
    };
    结构B:A{};
    
    void bar(A&A){std::cout由于重载是在编译时解决的,因此您需要向编译器提供足够的信息,以决定要调用的
    bar
    的正确重载。由于您希望根据对象的运行时类型动态地做出决定,虚拟函数将非常有帮助:

    struct A {
        virtual void bar() { bar(*this); }
    };
    
    struct B : public A {
        virtual void bar() { bar(*this); }
    };
    
    看起来主体是相同的,因此可以消除
    B::bar
    ,但事实并非如此:虽然主体看起来完全相同,但由于C++中重载的静态解析,它们调用不同的
    bar
    s:

    • A::bar
      中,
      *此
      的类型是
      A&
      ,因此调用第一个重载
    • B::bar
      中,
      *此
      的类型为
      B&
      ,因此调用第二个重载
    修改调用代码以调用成员
    bar
    将完成更改:

    std::list<A *> l;
    l.push_back(new B());
    l.push_back(new B());
    for (std::list<A *>::iterator it = l.begin(); it != l.end(); ++it)
        (*it)->bar();
    
    std::list l;
    l、 向后推(新B());
    l、 向后推(新B());
    对于(std::list::iterator it=l.begin();it!=l.end();++it)
    (*it)->bar();
    
    如果
    **它的类型是
    A
    ,那么您为什么期望“这是一个B”?使用虚拟方法
    A::bar()
    B::bar()
    您将看到您期望的结果。因为我在其中分配了一个B。这无关紧要。重载解析如何知道指向
    a
    的指针实际上指向a
    B
    ?重载解析发生在编译时。@LukeSkywalker:如果您想让重载在基类指针上正常工作,您必须使用虚拟机member functions.OP要求为非成员函数提供解决方案,这似乎不是XY问题。@luk32我相信我的答案在这里解决了这个问题。让我们看看OP的反应。Antonio,如果bar仍然是类之外的函数,并且由a和B的虚拟成员方法调用,我会接受这个答案(例如称为foo)它本身会调用bar。@如果允许我们更改类签名,当然可以。这是正确的方法。请注意,可以委托给一个自由函数;例如,自由的
    bar()
    可以保持OP的状态,而虚拟函数将是
    虚拟的void bar\u helper(){bar(*this);}
    ,您需要在每个类中编写它(您可以通过CRTP派生,使其看起来更整洁),老实说,这是OP将要走的路线。总是有更丑陋的解决方案使用类型安全的联合,如
    boost::variant
    另一个选项是由AndyG间接提供的。使用Virtual
    #include <iostream>
    #include <list>
    
    struct A {
        virtual ~A() {}   // important
    };
    
    struct B : A {};
    
    void bar(A &a) { std::cout << "This is an A" << std::endl; }
    void bar(B &b) { std::cout << "This is a B" << std::endl; }
    
    void bar_helper(A *ptr)
    {
        if ( auto b = dynamic_cast<B *>(ptr) )
            bar(*b);
        else
            bar(*ptr);
    }
    
    int main()
    {
        std::list<A *> ls;
        ls.push_back(new B);
        ls.push_back(new B);
        ls.push_back(new A);
    
        for (auto ptr : ls)
        {
            bar_helper(ptr);
            delete ptr;
        }
        ls.clear();
    }
    
    struct A {
        virtual void bar() { bar(*this); }
    };
    
    struct B : public A {
        virtual void bar() { bar(*this); }
    };
    
    std::list<A *> l;
    l.push_back(new B());
    l.push_back(new B());
    for (std::list<A *>::iterator it = l.begin(); it != l.end(); ++it)
        (*it)->bar();