【问题标题】:C++: How method calls with multiple polymorphic inputs are resolved?C++:如何解决具有多个多态输入的方法调用?
【发布时间】:2016-10-26 14:57:03
【问题描述】:

假设我们有这些重载的 C++ 函数:

void paint(Shape s, Color c) {}
void paint(Circle ci, Color c) {}
void paint(Shape s, SolidColor sc) {}

显然,ShapeCircle 的父级,ColorSolidColor 的父级。

如果我这样调用函数:paint(myCircle, mySolidColor) 将调用哪个版本的paint 函数?

通常,当层次结构中有多个参数可以是任何类型时,如何解决具有多个候选解析的方法调用?

(我希望我的问题+示例足够清楚,但如果有歧义请告诉我)

附:还有这个电话呢?

Color* c = create_color();  //returns SolidColor instance
Shape* s = create_shape();  //returns Circle instance
paint(s,c);

将调用哪个版本的paint?

【问题讨论】:

  • 很确定你会得到歧义错误。编译器会要求您进行显式转换。

标签: c++ class oop polymorphism overloading


【解决方案1】:

根据您的代码,元素将在传递给函数时被复制,并被切片:例如,在第一次重载中,如果您将 Circle 传递给 Circle 信息,Shape s 只会是松散的 Circle 信息. 如果Shape 是抽象的,则此代码甚至无法编译。

其要点在于,此代码不会像 Java / C# 中外观相似的代码那样实现多态行为。

第二点是重载决议发生在运行时多态之前:即编译器在编译时通过选择最匹配的函数原型来选择要调用的函数。

如果你有:

int main() { 
  Circle myCircle;
  SolidColor mySolidColor;
  paint(myCircle, mySolidColor);
}

然后编译器将 complain due to ambiguity 因为重载 2 和 3 都可以同样工作。

最重要的是,对于 C++ 中的多态性,您希望通过引用传递参数:

void paint(Shape& s, Color& c) {}
void paint(Circle& ci, Color& c) {}
void paint(Shape& s, SolidColor& sc) {}

【讨论】:

  • 关于抽象类和 pass-by-ref 我知道。我只是想让例子简短一些。如果我将ShapeColor 类型的变量传递给绘画,但在运行时,它们的类型将是CircleSolidColor。会调用哪个函数?
  • @MahdiMohammadinasab 如果变量的类型为ShapeColor,那么它们不能具有CircleSolidColor 的动态类型;他们将被切片。正如答案所暗示的那样,它们需要成为参考。
  • 变量的类型和对象的类型是有区别的。如果你有 Foo f; 之类的东西,那么 f Foo,仅此而已。如果您有Foo& fFoo* f,那么f 中可以有FooFoo 的子类型。
【解决方案2】:

如果有歧义请告诉我

模棱两可;正在通话中!

paint(Circle{}, SolidColor{});

这个调用是模棱两可的,因为没有任何重载比其他调用更专业化。 Clang 给出了这个错误:

main.cpp:11:5: error: call to 'paint' is ambiguous
    paint(Circle{}, SolidColor{});

通常,当层次结构中有多个参数可以是任何类型时,如何解决具有多个候选解析的方法调用?

这称为重载解决方案,并且主题过于庞大,无法在特定的 SO 答案中涵盖。 cppreference 关于该主题的文章应该为您提供合理的概述。

【讨论】:

    【解决方案3】:

    还有一个未解决的挑战!

    除了已经很出色的答案之外,值得一提的是您的方法中的两个问题:

    • 要使用运行时多态性,您需要通过指针(如果可能是智能指针)或引用传递参数。因为按值传递需要在编译时知道对象的大小。如果将孩子复制到父母身上,可能会导致切片。

    • 只有虚成员函数是多态的。非虚拟成员函数和非成员函数是在编译时根据对象的声明类型选择的。

    如何解决?用模板方法?

    要在非成员疼痛函数中引入运行时多态性,可以考虑使用template method design pattern

    class Shape {
    public: 
        virtual void paint_shape() = 0 ; 
        virtual ~Shape() {}     // polymorphic class => better have destructor being virtual 
    };    
    
    class Color {
    public: 
        virtual void set_color() = 0;
        virtual void reset_color() = 0;
        virtual ~Color() {}  
    };    
    ...
    void paint(Shape *s, Color *c) {
         activate_window (); 
         c->set_color();     // use polymorphic function 
         s->paint_shape(); 
         c->set_color();     // use polymorphic function 
         refresh_window(); 
    }
    

    这要求您的非成员函数可以仅在预定义的骨架的基础上表达,并且只能在某些阶段依靠单个对象的多态成员函数来实现多态性

    但是我可以做同时依赖于几种类型的多态吗?

    是的,但它稍微复杂一些。最初的想法是使用多级多态性和重载来设计一个乒乓球。它被称为double dispatch。一般的想法是这样的:

    class Color; 
    class Shape {
    public: 
        virtual void paint_color(Color *c) = 0 ; 
        virtual ~Shape() {} 
    };    
    class Circle : public Shape {
    public:
        void paint_color(Color *c) override;  
    };
    
    class Color {
    public: 
        virtual void paint_shape (Circle*) = 0;
        virtual void paint_shape (Square*) = 0;
        ...
        virtual ~Color() {}  
    };    
    class SolidColor : public Color {
    public: 
        void paint_shape (Circle*) override ;
        void paint_shape (Square*) override ;
    };    
    
    void Circle::paint_color(Color *c) {
        c->paint_shape (this);  // this is Circle*  => bounce on polymorphic color
    }                           //                     with real determined shape    
    void SolidColor::paint_shape(Circle *c) {
        paint_shape_and_color (c, this);  // this is SolidColor* => bounce on overload with 
    }                           //                     determined shape and color 
    
    
    void paint(Shape *s, Color *c) {
         s->paint_shape(c);   // polymorphic call 
    }
    void paint_shape_and_color (Circle *s, SolidColor *c) {
         ...  // real painting 
    }
    

    【讨论】:

      猜你喜欢
      • 2015-12-09
      • 1970-01-01
      • 2011-09-29
      • 2013-03-14
      • 1970-01-01
      • 2011-10-14
      • 2018-03-02
      • 2019-10-23
      相关资源
      最近更新 更多