2

来自 Bruce Eckel 的代码,用 C++ 思考

class A {
    int i;
  public:    
    A(int ii) : i(ii) {}
    ~A() {}
    void f() const {}
};

class B {
    int i;          
  public:
    B(int ii) : i(ii) {}
    ~B() {
    void f() const {}
};

class C : public B {
    A a;
  public:
    C(int ii) : B(ii), a(ii) {}
    ~C() {}  // Calls ~A() and ~B()
    void f() const {  // Redefinition
        a.f();
        B::f();
    }
};

int main() {
    C c(47);
}

对于他说的这个代码,

函数 C::f( ) 重新定义了它继承的 B::f( ),并且还调用了基类版本。此外,它调用 af()。请注意,您唯一可以谈论重新定义函数的时间是在继承期间。使用成员对象,您只能操作对象的公共接口,而不能重新定义它。

他什么意思 ?

该函数C::f()只是通过范围解析运算符调用f()B这是因为它是继承的,并且同名的函数也存在于C. A的函数f()是通过类中定义的对象调用的C

f()那么,正如 Eckel 所说,功能的重新定义在哪里?

4

3 回答 3

2

由于该类C派生自B,因此该函数通过定义自己的版本来C::f()覆盖该函数。B::f()如果你声明一个类型的对象C并调用它的f()函数,它将执行C::f()完全独立于B::f(). 所以基类函数被重新定义了。

请注意,类C还包含一个类型的成员A,它也有一个函数f(),并且它C的实现f()恰好是调用a.f()。所以C可以为自己f()提供不同的接口,但不能改变 A它的实现。

于 2013-06-22T17:48:34.497 回答
1

不同之处在于,如果您没有编写函数C::f,但仍然f在(静态)类型的对象上调用会发生什么C,如

C c;
c.f();

如果不存在C::f,这将调用B::f(发现是因为B是 的基类C),但不会调用(A::f因为它只是一个成员对象)。因此,函数的存在C::f会导致调用语义发生变化c.f()

但是请注意,C::f它不会覆盖 B::f,因为B::f它不是虚拟的。也就是说,下面的代码仍然会调用B::f,而不是C::f

C c;
B& b(c);
b.f();

如果B::f是虚拟的,C::f将覆盖它并且该代码将调用C::f. 但是由于B::f不是虚拟的,C::f因此不会覆盖它,因此上面的代码确实调用了B::f().

顺便说一句,我不同意 Bruce Eckel 的术语。在我看来,“重新定义”意味着替换定义。然而C::f并没有取代 的定义B::f,它只是隐藏了它。

于 2013-06-22T17:57:15.570 回答
0

如果你删除了“C::f”的定义,你仍然可以调用“C::f()”,因为它会被继承。但在这个例子中,他选择为 C 提供它自己的“f”实现。这对处理“C”类型对象的任何人“隐藏”了 B::f 函数。但它被称为“重新定义”,因为通过向下转换到父类型,该定义也对任何查看“C”类型对象的人隐藏。

考虑

#include <iostream>
using namespace std;

class A { void x() { cout << "A::x" << endl; } };
class A1 : public A {}; // No redefinition.
class A2 : public A { void x() { cout << "A2::x" << endl; A::x(); } };

int main(int argc, const char** argv)
{
    A1 a1;
    A2 a2;

    cout << "a1: "; a1.x();
    cout << "a2: "; a2.x();

    A2* pa2 = &a2;
    cout << " pa2: "; pa2->x();
    A* pa = (A*)a2;
    cout << "pa: " ; << pa;

    return 0;
}

最后一个——演员阵容——是最重要的部分。您只能通过拥有 A2 类型的对象或尚未重新定义它的后代来调用 A2 的“x”。想象一下,如果您创建了以下内容:

class Account {
   float m_balance;
public:
    Account() : m_balance(0) {}
    void Close() {} // What to do when the account is closing.
}

class SecureAccount : public Account {
    Transaction* m_transactionInProgress;
public:
    SecureAccount() : Account(), m_transactionInProgress(nullptr) {}
    void Close() {
        if(m_transactionInProgress) {
           m_transactionInProgress->Finish();
        }
    }
};

继承的全部意义在于让你使用最小公分母,所以在这种情况下,你很可能会调用

/*account*/p -> Close();

只能在Account中看到“关闭”的定义。因此,当您将 SecureAccount* 添加到向量中,并尝试在它仍有未完成交易时关闭它,您将蒙受损失。

为了了解如何解决这个问题,请继续阅读虚函数的解释。

于 2013-06-22T17:52:16.873 回答