我一直在研究这个问题,为即将到来的C ++期末考试做准备:
// What gets printed?
#include <iostream>
using namespace std;
class A {
public:
A(int a = 5) : i(a) { cout << "A" << endl; }
void foo() { cout << "this.i " << i << endl; }
virtual void print() const { cout << i << " in A" << endl; }
protected:
int i;
};
class B : public A {
public:
B() : A(1) { cout << "B default" << endl; }
void foo() { cout << i << " in B" << endl; }
void print() const { cout << i << " in B" << endl; }
};
int main() {
A *pa;
B b;
pa=&b;
pa->foo();
pa->print();
return 0;
}
它的输出是:
A
B default
this.i 1
1 in B
我理解正在打印的A
来自超级类B
中调用A
的构造函数,我理解指针*pa
指向{{ 1}}让它访问&b
的基类方法,但它如何在foo
而不是B::print()
中打印值?
答案 0 :(得分:2)
这是因为你在B中覆盖了print()。因为在类A中没有将foo()定义为虚拟,所以调用了类A中的foo()。
答案 1 :(得分:1)
由于B
来自A
,因此每个B
为A
。因此pa
可以指向类型为A
的对象或任何派生类型。但是对象的类型不会改变,因此b
仍然是B
类型并且表现如此。唯一的限制是,由于它是由A
指针引用的,因此只能引用在基类A
中声明的方法和成员变量。
有关为什么 C ++以这种方式工作的解释,请查看关于vtable
的讨论。
答案 2 :(得分:1)
virtual void print()
函数前面的 virtual 这个词使得多态性发挥了作用。当派生类实现一个在基类中声明为virtual的函数时,它将被调用而不是派生类实例的基类函数。
您创建派生类B的实例,而B有一个名为print()的成员函数,它将被调用。
答案 3 :(得分:0)
之间的区别
pa->foo();
和
pa->print();
是由一个简单的事实造成的:
指针的类型指示编译器如何解释在特定地址处找到的内存,以及解释应跨越多少内存
,来自书籍 Inside C ++对象模型。
换句话说,当编译器尝试翻译这行代码pa->foo()
时,他只知道 pa 是A类的指针而 foo 是类A的函数虽然我们知道实际上 pa 指向B类的内存块,但编译器却不知道这个事实。他只是将 pa 解析为A类的指针,并找到A的函数 foo 的定义。然而pa->print()
的神奇之处在于C ++的虚函数实现。对于普通函数,编译器只解析其名称并跳转到该函数的起始地址。但是,对于虚函数,编译器将首先从指针 pa 指向的内存中找到 vptr 指针,并解析 vptr 以查找正确定义 print 功能。因为这次编译器从内存中读取 vptr 并且内存实际上属于B类,所以将调用B的打印。
这是另一个例子来说明它:
// What gets printed?
#include <iostream>
using namespace std;
class A {
public:
int b;
A(int a = 5) : i(a) {
b = 42;
cout << "A" << endl;
}
void foo() { cout << "this.i " << i << endl; }
virtual void print() const { cout << i << " in A" << endl; }
protected:
int i;
};
class B : public A {
public:
int b;
B() : A(1) {
b = 43;
cout << "B default" << endl; }
void foo() { cout << i << " in B" << endl; }
void print() const { cout << i << " in B" << endl; }
};
int main() {
A *pa;
B b;
pa=&b;
cout << pa->b << endl;
cout << b.A::b << ", " << b.b << endl;
//pa->foo();
//pa->print();
return 0;
}
输出
A
B default
42
42, 43
顺便说一句,虚函数机制仅适用于指针或引用,因为对于对象A,A.print()
必须在一个块中解析为 vptr A类的记忆是A的打印功能