C++ 虚函数表现怪异
C++ Virtual Functions Acting Weird
我想了解虚函数和虚继承。在大多数情况下,我认为我成功地掌握了它以及它与多态性的关系,我一直在阅读 vptr 如何与派生对象一起工作等等,但是下面的例子让我失望了,它是我在算法和C++ 书中的数据结构:
#include <iostream>
using namespace std;
class Class1 {
public:
virtual void f() {
cout << "Function f() in Class1\n";
}
void g() {
cout << "Function g() in Class1\n";
}
};
class Class2 {
public:
virtual void f() {
cout << "Function f() in Class2\n";
}
void g() {
cout << "Function g() in Class2\n";
}
};
class Class3 {
public:
virtual void h() {
cout << "Function h() in Class3\n";
}
};
int main() {
Class1 object1, *p;
Class2 object2;
Class3 object3;
p = &object1;
p->f();
p->g();
p = (Class1*) &object2;
p->f();
p->g();
p = (Class1*) &object3;
p->f(); // possibly abnormal program termination;
p->g();
// p->h(); // h() is not a member of Class1;
return 0;
}
输出:
Function f() in Class1
Function g() in Class1
Function f() in Class2
Function g() in Class1
Function h() in Class3
Function g() in Class1
除了最后一个p->f();
我什么都懂。作为序言,我知道我们不能直接从 p
调用 h()
因为转换为 Class1
类型,但是 Class3
vptr 不应该只指向虚函数 h()
在它的 vtable 中,如果是这样,那么它不应该在 Class3's
vtable 中寻找 f()
而没有找到它吗?为什么它认为 Class1::f()
是 Class3::h()
,它不像 Class3
是从 Class1
继承的……为了记录,如果我们将 Class3
重写为是:
class Class3 : public Class1 { // publicly inherit from Class1 is only difference
public:
virtual void h() {
cout << "Function h() in Class3\n";
}
};
并向上转换为 Class1
指针,然后调用 p->f()
它按预期给了我们 Class1::f()
,我只是不明白为什么它甚至让我们调用 p->f()
当 Class3
不继承自 Class1
.
此示例不好,您正在将不相关的类型转换为彼此。这会导致未定义的行为。
该代码利用了不同对象类型之间假定的布局相似性。 vtable 将在每个对象中位于相同的位置,虚函数将通过索引而不是名称找到。因此,对 Class1
的第一个虚函数的调用会通过 table 生成调用,从而导致对 Class3
的第一个虚函数的调用。请记住,这是一个意外,并且不能由 C++ 的任何属性保证。
I just cannot figure out why it even lets us call p->f()
when Class3
does not inherit from Class1
.
这正是您要问的。这个问题与虚拟继承或虚拟功能无关。您问题中除上述文字外的所有内容均完全无关。
这个问题实际上是为什么不阻止您在编译时执行以下操作:
struct Foo
{
void foo() {}
};
struct Bar
{
void bar() {}
};
int main()
{
Foo f;
((Bar*)&f)->bar();
}
答案是,这就是 C 风格的强制转换 :它们允许您覆盖类型系统。由您来做对,而不是像您现在所做的那样对编译器撒谎。通过这样做,您的程序具有 未定义的行为,仅此而已。
您或本书的作者可能只是忘记了 parent/child 继承关系,使此示例代码除了未定义的行为之外还有其他价值:)
#include <iostream>
using namespace std;
class Class1 {
public:
virtual void f() {
cout << "Function f() in Class1\n";
}
void g() {
cout << "Function g() in Class1\n";
}
};
class Class2 : public Class1 {
public:
virtual void f() {
cout << "Function f() in Class2\n";
}
void g() {
cout << "Function g() in Class2\n";
}
};
class Class3 : public Class1 {
public:
virtual void h() {
cout << "Function h() in Class3\n";
}
};
int main() {
Class1 object1, *p;
Class2 object2;
Class3 object3;
p = &object1;
p->f();
p->g();
p = (Class1*) &object2;
p->f();
p->g();
p = (Class1*) &object3;
p->f();
p->g();
// p->h(); // h() is not a member of Class1;
return 0;
}
打印:
Function f() in Class1
Function g() in Class1
Function f() in Class2
Function g() in Class1
Function f() in Class1
Function g() in Class1
这是一个不错的基本多态性示例。
我想了解虚函数和虚继承。在大多数情况下,我认为我成功地掌握了它以及它与多态性的关系,我一直在阅读 vptr 如何与派生对象一起工作等等,但是下面的例子让我失望了,它是我在算法和C++ 书中的数据结构:
#include <iostream>
using namespace std;
class Class1 {
public:
virtual void f() {
cout << "Function f() in Class1\n";
}
void g() {
cout << "Function g() in Class1\n";
}
};
class Class2 {
public:
virtual void f() {
cout << "Function f() in Class2\n";
}
void g() {
cout << "Function g() in Class2\n";
}
};
class Class3 {
public:
virtual void h() {
cout << "Function h() in Class3\n";
}
};
int main() {
Class1 object1, *p;
Class2 object2;
Class3 object3;
p = &object1;
p->f();
p->g();
p = (Class1*) &object2;
p->f();
p->g();
p = (Class1*) &object3;
p->f(); // possibly abnormal program termination;
p->g();
// p->h(); // h() is not a member of Class1;
return 0;
}
输出:
Function f() in Class1
Function g() in Class1
Function f() in Class2
Function g() in Class1
Function h() in Class3
Function g() in Class1
除了最后一个p->f();
我什么都懂。作为序言,我知道我们不能直接从 p
调用 h()
因为转换为 Class1
类型,但是 Class3
vptr 不应该只指向虚函数 h()
在它的 vtable 中,如果是这样,那么它不应该在 Class3's
vtable 中寻找 f()
而没有找到它吗?为什么它认为 Class1::f()
是 Class3::h()
,它不像 Class3
是从 Class1
继承的……为了记录,如果我们将 Class3
重写为是:
class Class3 : public Class1 { // publicly inherit from Class1 is only difference
public:
virtual void h() {
cout << "Function h() in Class3\n";
}
};
并向上转换为 Class1
指针,然后调用 p->f()
它按预期给了我们 Class1::f()
,我只是不明白为什么它甚至让我们调用 p->f()
当 Class3
不继承自 Class1
.
此示例不好,您正在将不相关的类型转换为彼此。这会导致未定义的行为。
该代码利用了不同对象类型之间假定的布局相似性。 vtable 将在每个对象中位于相同的位置,虚函数将通过索引而不是名称找到。因此,对 Class1
的第一个虚函数的调用会通过 table 生成调用,从而导致对 Class3
的第一个虚函数的调用。请记住,这是一个意外,并且不能由 C++ 的任何属性保证。
I just cannot figure out why it even lets us call
p->f()
whenClass3
does not inherit fromClass1
.
这正是您要问的。这个问题与虚拟继承或虚拟功能无关。您问题中除上述文字外的所有内容均完全无关。
这个问题实际上是为什么不阻止您在编译时执行以下操作:
struct Foo
{
void foo() {}
};
struct Bar
{
void bar() {}
};
int main()
{
Foo f;
((Bar*)&f)->bar();
}
答案是,这就是 C 风格的强制转换 :它们允许您覆盖类型系统。由您来做对,而不是像您现在所做的那样对编译器撒谎。通过这样做,您的程序具有 未定义的行为,仅此而已。
您或本书的作者可能只是忘记了 parent/child 继承关系,使此示例代码除了未定义的行为之外还有其他价值:)
#include <iostream>
using namespace std;
class Class1 {
public:
virtual void f() {
cout << "Function f() in Class1\n";
}
void g() {
cout << "Function g() in Class1\n";
}
};
class Class2 : public Class1 {
public:
virtual void f() {
cout << "Function f() in Class2\n";
}
void g() {
cout << "Function g() in Class2\n";
}
};
class Class3 : public Class1 {
public:
virtual void h() {
cout << "Function h() in Class3\n";
}
};
int main() {
Class1 object1, *p;
Class2 object2;
Class3 object3;
p = &object1;
p->f();
p->g();
p = (Class1*) &object2;
p->f();
p->g();
p = (Class1*) &object3;
p->f();
p->g();
// p->h(); // h() is not a member of Class1;
return 0;
}
打印:
Function f() in Class1
Function g() in Class1
Function f() in Class2
Function g() in Class1
Function f() in Class1
Function g() in Class1
这是一个不错的基本多态性示例。