首页 > 试题广场 >

class B { public: virtual void

[单选题]
class B {
public:
  virtual void foo() {
      cout << "B" << a;
  }
  int a = 1;
};
class D : public B{
public:
  D() {
    a = 2;
  }
  virtual void foo() {
      cout << "D" << a;
  }
};

B x = D();  // 请务必仔细看这一行
x.foo();

这段代码输出什么?
  • B1
  • B2
  • D1
  • D2

关键点:对象切片(Object Slicing)

这里最重要的概念是对象切片。当我们写:

B x = D();

这里发生的是:创建一个D的临时对象,然后用这个临时对象来初始化x。由于x的类型是B,而不是B*或B&,所以这里会发生切片——即D对象中属于B的部分会被复制到x中,而D特有的部分会被“切掉”。

这意味着:
1.x是一个B类型的对象,不是一个D类型的对象。
2.D的构造函数会被调用(因为先创建D的临时对象),但x最终只是一个B。

虚函数调用的动态绑定

虚函数的动态绑定(即多态)只在通过指针或引用调用时生效。在这里,x是一个对象,不是指针或引用。因此,x.foo()的调用会在编译时就确定调用B::foo(),因为x的静态类型是B。

成员变量a的值

在D的构造函数中,a被设置为2。但由于x是一个B对象,它只会包含B的子对象。D的构造函数确实会被调用(因为先创建D的临时对象),但a的设置是在D的构造函数中进行的。然而,x是B类型,所以a的值取决于B的部分。

实际上,当D的临时对象被创建时:
1. 首先调用B的构造函数(隐式默认构造函数),将a初始化为1。
2. 然后调用D的构造函数,将a改为2。
3. 然后这个D对象被切片,复制其B部分到x中。因此x.a会是2(因为D的构造函数已经运行,将a改为2)。

验证步骤

让我们一步步验证:

1. 创建D的临时对象:

  • 调用B的构造函数:a = 1。
  • 调用D的构造函数:a = 2。
  • 此时临时对象的a是2。

2. 用临时对象初始化x(B x = D();):

  • 这里会调用B的复制构造函数(或移动构造函数,但这里不重要),将临时对象的B部分复制到x中。
  • 因此x.a是2。

3. 调用x.foo():

  • 由于x是B对象,不是指针或引用,所以调用B::foo()。
  • B::foo()打印"B"和x.a(即2)。

正确输出

因此,输出是:

B2

进一步对比

如果改为指针或引用,行为会不同:

D d;
B* ptr = &d;
ptr->foo();  // 输出 D2,因为多态生效

B& ref = d;
ref.foo();   // 输出 D2,因为多态生效

但原问题中是对象,所以不会多态。

总结

  • 对象切片导致x只是一个B对象。
  • 虚函数对非指针/引用不生效,调用B::foo()。
  • D的构造函数运行后a是2,切片后x.a是2。

最终答案

输出:

B2
编辑于 2025-11-03 15:07:37 回复(0)