class A {
public:
A() { foo(); }
~A() { foo(); }
void foo() { cout << 3; }
void bar() { foo(); }
};
class B : public A {
void foo() { cout << 2; }
};
int main() {
B b;
b.bar();
return 0 ;
}
我编译并运行它。结果是333 ......但我想:当我打电话给b.bar()时。它将直接指向bar(),然后调用类B中的foo()函数,因为类A中的foo()在类B中被重写。结果我认为是323。但是我错了。我错过了什么吗?请帮我解释它的实际工作原理@
答案 0 :(得分:3)
问题在于你有一个非虚拟foo()
,以便A::bar()
会调用它知道的唯一foo()
,即A::foo()
,即使它是调用它的B 。
尝试:
class A {
public:
A() { foo(); }
virtual ~A() { foo(); } // <<---------- recommendation
virtual void foo() { cout << 3; } // <<<--------- solves it
void bar() { foo(); }
};
class B : public A {
void foo() override { cout << 2; } // <<---------- recommendation
};
其他信息:
在基类中创建foo()
virtual允许每个类重写此函数,并确保调用的foo()
将是foo()
对应的对象的真实类。
然后在派生类中使用关键字override
是good practice:它不是强制性的,但如果你在函数签名中输入拼写错误,你会立即注意到编译 - 时间错误消息。
另一个好的做法是,如果类中至少有一个虚函数,则使基类析构函数为虚拟。
最后一句话:在B中,foo()
是私人的。这是合法的,但它很奇怪,因为继承说B是一种A,但你不能完全使用B对象作为A对象。
答案 1 :(得分:0)
成员A::foo
是非虚拟的,因此在任何地方都会被静态绑定。因此,在编译A::bar
时,对foo()
的调用将(静态地)绑定到实现A::foo()
。 A::foo
中的此静态绑定不会因您稍后创建派生类B
的实例而更改。
但是,如果您在主要地方呼叫b.foo()
,则B::foo
将被绑定。
要通过B::foo
调用A::bar
,您必须将A::foo
声明为`virtual:
class A {
public:
A() { foo(); }
virtual ~A() { foo(); }
virtual void foo() { cout << 3; }
void bar() { foo(); }
};
请注意,您还要将析构函数声明为虚拟;非虚拟析构函数很少有意义。
答案 2 :(得分:-1)
您必须包含虚拟以覆盖存储在A。
中的功能添加到
virtual void foo() { cout << 3; }
和B
void foo() override { cout << 2; }
应该做的伎俩。虚函数是成员函数,其行为可以在派生类中重写。与非虚函数相反,即使没有关于类的实际类型的编译时信息,也会保留被覆盖的行为。如果使用指针或对基类的引用处理派生类,则对重写的虚函数的调用将调用派生类中定义的行为。