据我了解,带引用的多态性应该与指针完全一样。
但是,请考虑以下示例:使用指针时正确调度doer()
的调用,但在使用引用时似乎都会调用“B版本”。
我无法弄清楚以下示例的行为方式。在使用引用时,为什么在这两种情况下都会调用“B version”?
#include <iostream>
class B;
class C;
void doer(B *x) {
std::cout << "B version" << std::endl;
}
void doer(C *x) {
std::cout << "C version" << std::endl;
}
class A {
public:
virtual ~A() {}
virtual void doit() = 0;
};
class B: public A {
public:
virtual void doit() override {
doer(this);
}
};
class C: public A {
public:
virtual void doit() override {
doer(this);
}
};
int main() {
B b;
C c;
A *a = &b;
a->doit(); // B version gets called, OK
a = &c;
a->doit(); // C version is called, OK
A &d = b;
d.doit(); // B version is called, OK
d = c;
d.doit(); // B version is called again??
}
答案 0 :(得分:3)
在这里指定一个参考:
A &d = b;
d.doit(); // B version is called, OK
在这里用c覆盖d所引用的对象(它不再是引用的定义):
d = c;
d.doit(); // B version is called again??
这是引用和指针之间的主要区别。引用就像一个常量指针,只能在定义时分配。之后,无论何时使用引用,都表示您引用的对象。
事实上当你d = c;
进行某些切片时。对象d实际上是B,但是来自A的operator=
被调用,只复制A的成员数据。
这里如何演示这句话:
class A {
public:
...
A& operator= (A a) {
cout << "A::operator=" << endl; // just to show what happens when d=c is called
return *this;
}
};
class B : public A {
public:
int x; // add some variables for B
virtual void doit() override {
cout << "B::doit() " << x << endl;
doer(this);
}
};
class C : public A {
public:
int a,b; // add aditional variables for C
virtual void doit() override {
cout << "C::doit() " << a << ","<<b << endl;
doer(this);
}
};
...
b.x = 123; // put some class specific variables in C
c.a = 222; c.b = 333; // put some class specific variables in C
A &d = b; // assignement of the reference. d reffers to a b object
d.doit(); // B version is called, OK
d = c; // but an A object is copied (so only A subobject of c is taken
// to overwrite A subobject of d)
d.doit(); // B version is called becaus again?? => yes !! because it's still a B
// And you see that the B part of the object is left intact by A::operator=
cout << typeid(d).name() << endl;
// remember at this point that d still refers to b !
答案 1 :(得分:1)
引用在其整个生命周期中都被它们的引用绑定,并且与指针不同,它需要初始化。使用赋值运算符调用引用的赋值运算符,并不重新分配引用:
A &d = b;
d = c;
此处d = c
从A
中包含的基类d
子对象调用赋值运算符,并复制A
中包含的c
子对象数据。 / p>