虚拟赋值运算符重载——如何选择正确的重载函数?

Posted

技术标签:

【中文标题】虚拟赋值运算符重载——如何选择正确的重载函数?【英文标题】:Virtual assignment operator overloading- how the correct overloaded function is chosen? 【发布时间】:2013-02-17 06:14:23 【问题描述】:

以下代码(来自 C++ FAQs 24.11)正在实现虚拟赋值运算符重载和覆盖:

#include <iostream>
using namespace std;

class B
public:
    virtual ~B() throw();
    virtual B& operator= (const B& b) throw();
;

B::~B() throw()

B& B::operator= (const B& b) throw()
 cout << "B::operator=(const B&)\n"; return *this; 


class D : public B
public:
    virtual D& operator= (const B& b) throw();
    D& operator= (const D& d) throw();
;

D& D::operator= (const B& b) throw()
cout << "D::operator= (const B&)\n"; return *this;

D& D::operator= (const D& d) throw()
cout << "D::operator= (const D&)\n"; return *this;


void sample(D& d, B& b, D& d2, B& b2)
    cout << "d = d2:   "; d = d2;
    cout << "d = b2:   "; d = b2;
    cout << "b = b2:   "; b = b2;
    cout << "b = d2:   "; b = d2;



int main()

    D d, b, d2, b2;
    sample(d,b,d2,b2);

它说输出是:

d = d2: D::operator=(const D&) d = b2: D::operator=(const B&) b = b2: D::operator=(const B&) b = d2: D::operator=(const B&)

上面写着:

"因为编译器会根据 参数的静态类型,第一个赋值是唯一的 调用接受 D 的赋值运算符;其他的都结束了 调用带 B 的赋值运算符。”

"最后两个调用解析为覆盖 (D::operator= (const B&)) 因为 sample() 中 b 的实际类是 D。如果 b 实际上是 a B,最后两个调用将解析为 (B::operator= (const B&))"

我有点迷糊,第一段说编译器看参数静态类型来确定使用哪个(重载?)函数调用,那么为什么最后一个case调用B参数类型的操作符,当传递的参数 d2 在 sample() 中被声明为类型 D&amp; d2

编辑参考下面的答案,我看不出对 B::=(D) 的请求如何导致 D::=(B)。如果有另一个子类 E 怎么办?为什么会调用 D::=(B) 而不是 E::=(B)?我们是说如果 B 没有参数 (D) 的函数“=”,那么运行时开始查看是否有任何派生对象包含这样的签名?

【问题讨论】:

【参考方案1】:

重载解决方案是使用静态类型完成的。一旦选择了重载,动态类型将用于确定使用哪个重写版本的函数。让我解释一下每个结果:

//Every object is a D object, so in
//each case, we expect the virtual calls
//to dispatch to the D version.

//lhs: D, rhs: D, so we expect
//D::operator=(D const&)
cout << "d = d2:   "; d = d2;

//lhs: D, rhs: B, so we expect
//D::operator=(B const&)
cout << "d = b2:   "; d = b2;

//lhs: B, rhs: B, so we expect
//B::operator=(B const&).
//This is virtual, and has been overridden
//in D, so the D version gets called.
cout << "b = b2:   "; b = b2;

//lhs: B, rhs: D, so we expect
//B::operator(D const&).
//This does not exist, so the closest match is used:
//B::operator(B const&). (This is a legal match, 
//because D is a subclass of B).
//This is virtual, and has been overridden
//in D, so the D version gets called.
cout << "b = d2:   "; b = d2;

【讨论】:

谢谢 - 一个问题。在#3中,我了解派生中提供了一个虚拟覆盖形式,但是为什么这突然使基类b中的版本过时了?我可以理解对象类型是否为 D,但它们是基类 B? B 对象肯定会使用 B 中的赋值运算符吗?我知道覆盖,但我不认为在这种情况下会调用 D 中的覆盖版本? 进一步说明我的观点——如果你有另一个派生类,称之为 E,它会覆盖 operator=(就像 D 一样).... E::operator(const B&) ,B怎么知道叫D版还是E版? @user997112:在任何情况下,对象的动态类型都是D不是 B)——你在main中构造了D对象,并且通过引用将它们传递给函数,因此动态类型没有改变。您已将函数标记为virtualvirtual 的意思是“根据调用函数的对象的动态类型进行调度”。如果您将 E 对象作为 bb2 参数传入,则将调用 E 版本。

以上是关于虚拟赋值运算符重载——如何选择正确的重载函数?的主要内容,如果未能解决你的问题,请参考以下文章

重载赋值运算符 - 多态容器

重载“着色器”类中的赋值运算符

赋值运算符重载函数 引用返回 与 对象返回

c++ 拷贝构造函数与赋值运算符重载函数的区别是

C++中赋值运算操作符和=重载有啥区别?

c++中拷贝构造函数和赋值运算符重载本质上一样么