0

我一直在研究这个问题,为即将到来的 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指向&b使其访问基类方法foo,但它是如何打印值B::print()而不是A::print()

4

4 回答 4

2

这是因为,您在 B 中覆盖了 print()。由于 foo() 在 A 类中未定义为虚拟,因此 A 类中的 foo() 被调用。

于 2013-06-09T20:11:36.007 回答
1

既然B是从 派生的A,那么每一个B 都是一个A。所以pa可以指向一个对象类型A或任何派生类型。但是对象的类型没有改变,所以b仍然是类型B并且表现得如此。唯一的限制是,由于它被指向 的指针引用A,因此您只能引用在基类中声明的方法和成员变量A

有关C++为何以这种方式工作的解释,请查看关于vtables 的讨论。

于 2013-06-09T20:03:31.847 回答
1
virtual void print()

function 前面的virtual一词使多态性发挥了作用。当派生类实现在基类中声明为虚拟的函数时,它将被调用而不是派生类实例的基类函数。

您创建派生类 B 的实例,并且 B 有一个名为 print() 的成员函数,它将被调用。

于 2013-06-09T20:17:16.040 回答
0

The difference between

pa->foo();

and

pa->print();

is caused by a simple fact:

The type of a pointer instructs the compiler as to how to interpret the memory found at a particular address and also just how much memory that interpretation should span

, from book Inside the C++ object model.

In other words, when compiler tries to translate this line of code pa->foo(), he only knows that pa is a pointer of class A and foo is function of class A. Although we know in fact pa points to a block of memory of class B, compiler doesn't and can't know that fact. He just resolve pa as a pointer of class A and find A's definition of function foo. Yet the magic of pa->print() is due to the virtual function implementation of C++. For a normal function, compiler just parse its name and jump to the start address of that function. However, for a virtual function, compiler will first find the vptr pointer from the memory pointed by the pointer pa and resolve the vptr to find the right definition of print function. Because this time compiler reads the vptr from memory and the memory is in fact belong to class B, B's print will be called.
Here is another example to illustrate it:

// 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;
 }

The output is

A
B default
42
42, 43

By the way, virtual function mechanism only works on pointer or reference, because for a object A, A.print() must resolve to vptr in a block of memory of class A which is A's print function

于 2013-06-10T08:38:45.207 回答