#include <iostream>
#include <string>
class A
{
public:
A(int a) : _a(a) {}
virtual ~A() {}
virtual void f() const {std::cout << _a << std::endl;}
private:
int _a;
};
class B : public A
{
public:
B(int a, int b) : A(a), _b(b) {}
virtual void f() const {std::cout << _b << std::endl;}
private:
int _b;
};
int main()
{
B b (1,2);
A a (5);
A& ref = a;
ref = b;
ref.f();
return 0;
}
输出:
1
据我所知,在将派生(扩展)类对象复制到基类对象时,会剪切派生对象并仅复制基类数据。但我认为'ref'的虚拟表现在应该是'b'的虚拟表,所以'ref.f();'应该调用函数:
void B::f() const {std::cout << _b << std::endl;}
但是在复制之后'ref'的vtbl仍然是A类的vtbl。为什么? 感谢。
答案 0 :(得分:3)
永远不会复制存储在每个多态对象中的虚拟表(或更准确地说,指向虚拟表的指针)。
你的“为什么”问题背后的原因并不十分清楚。 当然,它不会被复制。为什么要复制?虚拟表是一种描述具体对象的特定于类型的行为的方法。它基本上[间接]描述了对象的实际类型。由于在复制期间对象的实际类型不会更改,因此虚拟表保持不变。
答案 1 :(得分:3)
首先,'虚拟表'不是标准的C ++概念。它是实现动态绑定和实现虚函数的高度实现特定机制。
话虽如此,
但我认为是虚拟表 'ref'现在应该是虚拟的 表'b'所以'ref.f();'应该打电话 功能
这不正确。虚拟表是每个类而不是每个对象。每个对象只有Vptr。
'ref'的类型(如果你愿意,由typeid(ref).name确认)是'A&amp;'。当您指定'ref = b'时,将使用对象'b'作为参数调用'A'的隐式赋值运算符。该运算符只是盲目地将'b'的'A'子对象复制到'ref'引用的当前对象中(即'a')。因此,对象'a'现在具有与'b'的'A'子对象完全相同的状态。
正如您所看到的,在整个非常长的故事中,VTABLE和VPTR根本不存在!。
答案 2 :(得分:2)
如果使用指针(而不是refences),派生对象将保持原样。然后正确调用vtbl。
A* ref;
ref = &b;
ref->f();
Wen你使用引用,运行时系统不明白你在ref中的内容是B
。当你完成任务时,它认为它是A
。
答案 3 :(得分:1)
不,虚拟表不作为指针,并且在分配给基类实例时不会被复制(这称为切片)。
首先,让我们澄清您的代码:
A& ref = a;
ref = b;
直接相当于:
a = b;
现在,这里发生的是A
实例的内容被A
实例内容的B
部分替换。但是,结果仍然是A
实例,因此,它指向A
的虚拟表,正确调用A::f
。
答案 4 :(得分:0)
A& ref = a;
ref = b;
您的参考的静态和动态类型为A&
。赋值ref = b
不会根据您的期望更改引用的动态/静态类型。
来自Marshall Cline的C ++ FAQ
您无法将引用与引用分开。
与指针不同,一旦引用绑定到对象,it can not be "reseated" to another object.在这个意义上,引用类似于const指针,例如
int* const p
。
试试这个
A& ref = b; //reference is bound to b [dynamic type of the referent is `B&`])
ref.f(); //Prints 2
答案 5 :(得分:0)
我认为你对Reference vs. Pointer感到困惑。让自己了解代码实际上做什么。
A& ref = a; // Reference
ref = b;
在第一行之后,ref
和a
是同一对象的两个名称 。 ref
的有效类型为A
。
第二行将对象b
分配给ref
引用的对象(当然,a
)。你现在有了
b
和A
- b
的一部分复制到对象a
中(并由ref
引用)。复制后,复制对象的任何“B
- ness”都会完全丢失,以及对象a
的先前内容。 _a
成员被复制,_b
成员和B
vtable被拼接掉了。
请改为尝试:
int main()
{
B b (1,2);
A a (5);
A * ref = &a; // pointer, not reference
ref = &b;
ref->f();
return 0;
}
这可能会产生“2”,正如您可能预期的那样。它也不会覆盖对象a
,就像第一个例子那样。