在C ++中用继承重新定义函数的实际意义

时间:2013-06-22 17:34:08

标签: c++ inheritance

来自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(),并且还调用了基类版本。另外,它调用a.f()。请注意,唯一可以谈论重新定义函数的时间是在继承期间;使用成员对象,您只能操作对象的公共接口,而不是重新定义它。

他的意思是什么?

函数C::f()只是通过范围解析运算符调用f()的{​​{1}}。这是因为它是继承的,B中也存在具有相同名称的函数。 C的函数A通过类f()中定义的对象调用。

那么,正如Eckel所说,重新定义函数C在哪里?

3 个答案:

答案 0 :(得分:2)

由于类C派生自B,因此函数C::f()会通过定义自己的版本来覆盖函数B::f()。如果您声明类型为C的对象并调用其f()函数,则它将执行C::f(),它可以完全独立于B::f()。所以基类功能已经重新定义。

请注意,类C还包含A类型的成员,该成员还具有函数f(),以及C f()的实现恰好打电话给a.f()。因此C可以为自己的f()提供不同的接口,但它不能更改 A的实现。

答案 1 :(得分:1)

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

C c;
c.f();

如果不存在C::f,则会调用B::f(因为BC的基类而找到),但不是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()不会覆盖它,因此上面的代码会调用C::f

顺便说一下,我不同意Bruce Eckel的术语。在我看来,“重新定义”将意味着取代定义。但是B::f不会取代{{1}}的定义,而是隐藏它。

答案 2 :(得分: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();

只能在帐户中看到“关闭”的定义。因此,当您向向量添加SecureAccount *并尝试关闭它时,它仍然有一个打开的交易,您将会赔钱。

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