3

我在这里阅读 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++ 在类中看不到void foo(double a)函数Derived

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

4

6 回答 6

3

这是Function Hiding的经典示例。当且仅当以下情况下
,派生类中的函数会覆盖基类函数:

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

第二条规则有一个例外,即允许 协变返回类型。

鉴于以上两条规则:

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

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

于 2012-02-03T06:04:08.590 回答
2

ptr是类型Base*

作为参数的唯一功能Base是.1.0virtual void foo(int a)

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

于 2012-02-03T06:11:05.343 回答
2

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

C++ 确实看到了该函数,但由于函数签名差异,它Base::foo'svirtual ness 无关:

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). 因此它被称为。

于 2012-02-03T06:03:34.110 回答
1

C++ 无法void foo(double a)Derived类中看到 ,因为它没有覆盖具有类中签名的虚函数void foo(double a)Base您试图从中调用它)。

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


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

于 2012-02-03T06:02:06.603 回答
1
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);

}
于 2012-02-03T06:26:12.763 回答
0

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

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

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

于 2012-02-03T06:36:48.490 回答