为什么虚拟转让的行为不同于其他虚拟的功能相同的签名吗?
-
13-09-2019 - |
题
同时与执行一个虚拟分配的操作者,我已经结束了一个有趣的行为。它不是一个编译器故障,由于g++4.1、4.3和VS2005年共享相同的行为。
基本上,虚拟操作员=行为不同于任何其他虚拟函数关于的代码实际上是正在执行。
struct Base {
virtual Base& f( Base const & ) {
std::cout << "Base::f(Base const &)" << std::endl;
return *this;
}
virtual Base& operator=( Base const & ) {
std::cout << "Base::operator=(Base const &)" << std::endl;
return *this;
}
};
struct Derived : public Base {
virtual Base& f( Base const & ) {
std::cout << "Derived::f(Base const &)" << std::endl;
return *this;
}
virtual Base& operator=( Base const & ) {
std::cout << "Derived::operator=( Base const & )" << std::endl;
return *this;
}
};
int main() {
Derived a, b;
a.f( b ); // [0] outputs: Derived::f(Base const &) (expected result)
a = b; // [1] outputs: Base::operator=(Base const &)
Base & ba = a;
Base & bb = b;
ba = bb; // [2] outputs: Derived::operator=(Base const &)
Derived & da = a;
Derived & db = b;
da = db; // [3] outputs: Base::operator=(Base const &)
ba = da; // [4] outputs: Derived::operator=(Base const &)
da = ba; // [5] outputs: Derived::operator=(Base const &)
}
其效果是,虚拟操作员=具有不同的行为比任何其他虚拟功能用同样的签名([0]相比,[1]),通过调用的基础版本操作时,通过真正的派生的对象([1])或来源引用([3])虽然它并执行作为一个经常的虚拟功能时通过基准([2]),或者当的左值或rvalue是基本参考文献和其他来参考([4],[5]).
是否有任何合理的解释的这种奇怪的行为吗?
解决方案
事情是这样的:
如果我改变[1]
a = *((Base*)&b);
然后事情的方式工作。有一个自动产生的分配运营商 Derived
看起来是这样的:
Derived& operator=(Derived const & that) {
Base::operator=(that);
// rewrite all Derived members by using their assignment operator, for example
foo = that.foo;
bar = that.bar;
return *this;
}
在你的例汇编者有足够的信息来猜猜, a
和 b
是的类型 Derived
所以他们选择使用自动产生的操作员以上的呼叫你。这就是你如何了[1].我指的铸造力量编译器,来做你的方式,因为我告诉编译器"忘记", b
的类型 Derived
所以它使用 Base
.
其他结果可以解释的方式相同。
其他提示
有三个操作员=在这种情况下:
Base::operator=(Base const&) // virtual
Derived::operator=(Base const&) // virtual
Derived::operator=(Derived const&) // Compiler generated, calls Base::operator=(Base const&) directly
这就解释了为什么它看起来像基础:运营商=(基const&)是所谓的"几乎"在情况下,[1].这就是所谓从编译器生成的版本。这同样适用于种情况下[3].在2的情况下,右手边的参数'bb'具有类型基&,使得:操作员=(来&)无法被称为。
没有用户提供分配的操作者的定义对于源类。因此,编译器合成一个和境内基类分配操作人员被称为从该合成分配运营者对于源类。
virtual Base& operator=( Base const & ) //is not assignment operator for Derived
因此, a = b; // [1] outputs: Base::operator=(Base const &)
在得出类基类分配运营商已被复盖并因此被复盖的方法得到一条虚表的源类。当方法是通过调用基准或指针,然后得出类的复盖的方法被称为由于虚表项决议在运行时间。
ba = bb; // [2] outputs: Derived::operator=(Base const &)
==>境内==>(Object->虚表[赋员]) 得到该项分配操作人员在虚表的类目的属和调用的方法。
如果你失败提供一个合适的 operator=
(即正确的回返和参数类型),默认 operator=
提供由编译器,其重载用户定义的任何一个。在你的情况下,它会叫的 Base::operator= (Base const& )
在复制之前派生的成员。
检查这个 链接 详细信息,操作员=正在作出虚拟的。
原因是那里是编译器提供默认的分配 operator=
.这就是所谓的方案 a = b
因为我们知道,默认在内部调用基准分配运营商。
更多的解释有关虚拟转让可以发现: https://stackoverflow.com/a/26906275/3235055