来自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
在哪里?
答案 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
(因为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()
不会覆盖它,因此上面的代码会调用C::f
。
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 *并尝试关闭它时,它仍然有一个打开的交易,您将会赔钱。
为了了解如何解决这个问题,请继续阅读虚拟功能的解释。