这是我正在谈论的代码
#include <stdio.h>
#include <stdlib.h>
class A {
public:
void method() {printf("method A\n");}
virtual void parentMethod() { printf("parentMethod\n"); method(); }
};
class B : public A {
public:
void method() {printf("method B\n");}
};
int main(void) {
A a;
B b;
a.parentMethod();
b.parentMethod();
}
我的问题是为什么会这样?为什么在调用b.parentMethod()
时它不会打印method B
。我意识到它与method()
在A和B中存在以及非虚拟有关,但我无法理解它。有人能够解释这种行为吗?
感谢任何帮助。
答案 0 :(得分:2)
您的代码缺少virtual
关键字:
#include <stdio.h>
#include <stdlib.h>
class A {
public:
virtual void method() {printf("method A\n");} // virtual was missing
void parentMethod() { printf("parentMethod\n"); method(); } // unnecessary virtual keyword
};
class B : public A {
public:
void method() {printf("method B\n");}
};
int main(void) {
A a;
B b;
a.parentMethod();
b.parentMethod();
}
最上层的定义必须包含virtual
关键字。如果你考虑它,这是非常合乎逻辑的。在这种情况下,当您调用method()
时,编译器知道它必须立即执行比正常函数调用更多的操作。
否则,它必须查找并迭代所有派生类型,以查看它们是否包含method()
的重新定义。
答案 1 :(得分:2)
我的问题是为什么会这样?为什么在调用b.parentMethod()时它不会打印方法B.我意识到它有一些东西要做 使用method()在A和B中以及非虚拟,但是我 我无法理解它。有人能够解释这个 行为?
C ++在类/结构方面有两个间接层次。你有“普通功能”(包括“重载”,“lambdas”,静态等),你有“虚拟功能”。首先,让我们解释一下“普通函数”。
struct Foo {
void goo();
};
在这种结构中,goo
只是一个普通的旧函数。如果您尝试用C语言编写,这类似于调用
void goo(struct Foo *this);
没什么神奇的,只是一个带有“隐藏”指针的普通函数(所有c ++函数都将“隐藏”这个指针传递给它们)。现在,让我们在继承的结构中重新实现这个功能,
struct Goo : public Foo {
void goo();
};
...
Goo g;
g.goo();
Foo f;
f.goo();
此处,明确为日,g.goo()
在goo()
结构中调用Goo
,在Foo结构中调用f.goo()
调用goo()
。所以,在C函数中,这只是,
void goo(struct Foo *this);
void goo(struct Goo *this);
提供C进行参数重载。但仍然只是简单的功能。在Foo对象中调用goo()将调用与在Goo对象中调用goo()不同的函数。仅编译时间分辨率。但现在,让我们的功能“虚拟”
struct Foo {
virtual void goo();
};
struct Goo : public Foo {
void goo(); // <- also virtual because Foo::goo() is virtual
// In C++11 you'll want to write
// void goo() override;
// which verifies that you spelled function name correctly
// and are not making *new* virtual functions! common error!!
};
...
Goo g;
g.goo();
Foo f;
f.goo();
这里发生的是Foo现在包含一个“虚拟表”。编译器现在创建一个映射“最新”goo()函数位置的函数表。也就是说,隐式Foo()构造函数会执行类似的操作,
virt_table[goo_function_idx] = &Foo::goo;
和然后 Goo()中的构造函数将使用,
更新此表virt_table[goo_function_idx] = &Goo::goo;
然后当你有,
Foo *f = new Goo();
f->goo();
发生的事情类似于,
f->virt_table[goo_function_idx]();
在“虚拟表”中查找函数位置,并调用该函数。这意味着函数的运行时解析或多态。这就是Goo :: goo()的调用方式。
如果没有此表,编译器只能调用它对所述对象知道的函数。因此,在您的示例中,b.parentMethod()
在表中查找并调用。但是method()
不是该表的一部分,因此只尝试编译时解析。由于this
指针为A*
,因此您会调用A::method
。
我希望这会清除“虚拟桌”业务 - 它实际上是一个内部查找表,但仅适用于标记为virtual
的函数!
PS。您可能会问,“但是this
指针会被虚拟表从Foo *”转换为Goo *“,是的,它会。我会留下它作为练习,让你弄清楚为什么总是正确的。
答案 2 :(得分:1)
virtual
表示可以在子类中重写方法。我认为您希望method
覆盖parentMethod
,而不是B
。我已将parentMethod
重命名为foo
,以减少误解。
#include <stdio.h>
#include <stdlib.h>
class A {
public:
virtual void method() {printf("method A\n");}
void foo() { printf("foo\n"); method(); }
};
class B : public A {
public:
void method() {printf("method B\n");}
};
int main(void) {
A a;
B b;
a.foo();
b.foo();
}
你会看到这给出了预期的输出:
foo
method A
foo
method B
这是ideone。
答案 3 :(得分:1)
你是对的。发生这种情况是因为您的method
不是虚拟的。当您通过父类调用它时,它无法知道它已被重载,因此总是调用A::method
。如果您将method
标记为虚拟,则对其的调用将通过类vtable
进行路由,因此A::method
将被上升类中的B::method
替换。