【问题标题】:C++: Inheritance and overload resolutionC++:继承和重载决议
【发布时间】:2012-02-25 19:27:02
【问题描述】:

我在这里阅读 C++ 谜题:http://gotw.ca/gotw/005.htm

我不明白他对静态与动态重载解析(或默认参数)的解释,所以我尝试提炼问题并自己编写一些测试:

class Base {
    public:
    virtual void foo() {cout << "base/no parameters" << endl;}
    virtual void foo(int a) {cout << "base/int" << endl;}
};

class Derived : public Base {
    public:
    void foo() {cout << "derived/no parameters" << endl;}
    void foo(double a) {cout << "derived/int" << endl;}
};

int main() {
    Base* ptr = new Derived;
    ptr->foo();
    ptr->foo(1.0);
}

输出是:

derived/no parameters
base/int

怎么在对foo()的调用中,C++似乎识别出它指向Derived,但是在调用foo(1.0)时,C++在Derived类中没有看到void foo(double a)函数?

在我看来,有一些相互竞争的想法,即 C++ 具有多态性,这解释了第一次调用,但重载决议是在编译时完成的,这解释了第二次调用。

【问题讨论】:

    标签: c++ inheritance dynamic static overload-resolution


    【解决方案1】:

    这是 Function Hiding 的经典示例。
    派生类中的函数覆盖基类函数当且仅当:

    1. virtual 关键字至少存在于基类函数中。
    2. Derived 类中的函数与 Base 类函数具有完全相同的签名。

    第二条规则有一个例外,允许 Covariant return types

    鉴于以上两条规则:

    派生类中的无参数foo() 函数覆盖基类foo(),因此动态调度可以按您的预期工作。

    带有一个参数的foo(double) 版本不会覆盖基类foo(int) 的功能,它只是隐藏它。由于没有overidding,也就没有动态调度,编译器只调用它在基类范围内找到的foo(int) 函数。

    【讨论】:

    • 这回答了我的问题,您的链接可以帮助我理解更普遍的函数隐藏问题。谢谢
    【解决方案2】:

    C++ 在派生类中看不到void foo(double a) 函数?

    C++ 确实看到了该函数,但它由于函数签名差异而与Base::foovirtualness 无关

    virtual void Base::foo(int);  // 'Base' signature
    void Derived::foo(double);    // 'Derived' signature
    

    所以对于Derived::foo(double),这里有两个重要的事实:

    1. Base::foo(int) 无关(覆盖)
    2. 不是 virtual 方法(即使是 virtual 也无济于事)

    第一点更重要,因为当你打电话时

    // 'ptr' refers 'Base' method signature; so 'double' implicitly converts to 'int'
    ptr->foo(1.0);
    

    使用Base 指针。在 vtable 列表中,它只有一个 Base::foo(int) 条目。因此它被称为。

    【讨论】:

    • 我对vtable不是很熟悉,所以我一般想知道:当你创建一个指向对象的指针时,vtable是与指针关联还是与实际对象关联?
    • @newprogrammer,vtables 是特定于实现的细节,你不必担心它们;重要的是研究可观察行为;即何时应该调用 Base::foo(int) 而何时不调用。如果你对vtables感兴趣可以看this question
    【解决方案3】:

    ptr 的类型为 Base*

    Base 中唯一以1.0 为参数的函数是virtual void foo(int a)

    现在,请记住,对于要虚拟覆盖的函数,它需要完美匹配签名(减去方差,但不适用于您的情况)。您没有覆盖foo(int),而是实际上创建了一个新的foo(double)

    【讨论】:

    • ...但实际上创建了一个新的foo(double) 并在此过程中隐藏基类foo(int).
    【解决方案4】:

    C++ 看不到 Derived 类中的 void foo(double a),因为它覆盖了 Base 类中带有签名 void foo(double a) 的虚函数(您试图调用的地方)它来自)。

    Base 类中有一个void foo(int a),但根据 C++ 的规则,它是一个完全不同的函数。


    另一方面,Derived 类中的 void foo() 覆盖了 Base 类中的虚函数 void foo(),因为它们具有相同的签名。

    【讨论】:

      【解决方案5】:
      class Base {
          public:
          virtual void foo() {cout << "base/no parameters" << endl;}
          virtual void foo(int a) {cout << "base/int" << endl;}
              };
      
      class Derived : public Base {
          public:
          void foo() {cout << "derived/no parameters" << endl;}
          void foo(int a) {cout << "derived/int" << endl;}
           //  then your derived class function   will call 
      };
      
      int main() {
          Base* ptr = new Derived;
          ptr->foo();
          ptr->foo(1.0);
      
      }
      

      【讨论】:

        【解决方案6】:

        静态与动态重载解析 !!!这里是什么意思?

        重载总是静态绑定的,函数要在一个类中定义(没有一个函数在基类中,而另一个函数在派生类中)。

        所以 foo(double a) 和 foo(int a) 没有重载。同时,由于签名不同,这些都不会被覆盖。这就是为什么声明 ptr->foo(1.0); 没有发生虚拟机制的原因。

        【讨论】:

          猜你喜欢
          • 2017-11-20
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2012-04-05
          • 1970-01-01
          相关资源
          最近更新 更多