派生类继承基类赋值运算符?

Posted

技术标签:

【中文标题】派生类继承基类赋值运算符?【英文标题】:Derived class inherit base class assignment operator? 【发布时间】:2012-02-12 05:24:27 【问题描述】:

在我看来派生类不继承基类赋值运算符 如果派生类继承基类赋值运算符,你能解释一下下面的例子吗

在下面的代码中,我在 Derived 中重写了基类 operator=,以便 Derived 类默认赋值运算符调用重载的 operator=

#include <iostream>  
using namespace std;      
class Base  
  
    public:  
    Base(int lx = 0):x(lx)  
      
      

    virtual Base& operator=( const Base &rhs)  
      
        cout << "calling Assignment operator in Base" << endl;  
        return *this;  
    

    private:  
    int x;     
;      


class Derived : public Base  
  
    public:  
    Derived(int lx, int ly): Base(lx),y(ly)  
      
    

    Base& operator=(const Base &rhs)  
      
        cout << "Assignment operator in Derived"<< endl;  
        return *this;  
      

    private:  
    int y;    
;  



int main()  
  
    Derived d1(10,20);  
    Derived d2(30,40);  
    d1 = d2;  
  

它给出了输出

在 Base 中调用赋值运算符

我已经将基类 operator= 重写为派生类,所以如果派生类继承基类 operator= 那么它应该被 operator= 覆盖(我已经写在派生类中),现在派生类默认运算符= 应该调用被覆盖的版本,而不是来自基类 operator=。

【问题讨论】:

【参考方案1】:

引用标准(12.8.24):

因为复制/移动赋值运算符是隐式声明的 如果用户未声明类,则为基类复制/移动分配 运算符总是被相应的赋值运算符隐藏 派生类 (13.5.3)。引入的使用声明(7.3.3) 来自基类的赋值运算符,其参数类型为 可能是派生类的复制/移动赋值运算符 不被视为此类运算符的显式声明,并且确实 不抑制派生类运算符的隐式声明; 由 using 声明引入的运算符被 派生类中隐式声明的运算符。

【讨论】:

【参考方案2】:

如果你想实现多态行为,这意味着你的基方法被覆盖,你应该这样写:

int main ()
    Derived * d1 = new Derived (10,20);  
    Derived * d2 = new Derived (30,40);
    Base * b1 = d1 ;
    Base * b2 = d2 ; 
    *b1 = *b2 ;
    *d1 = *d2 ;
 

这将产生以下输出:

 $> Assignment operator in Derived.
 $> calling Assignment operator in Base. 

所以我在这里做的是隐式使用 c++ 动态转换。我不完全确定这是否有效,但为了说明我的观点,我认为它会很有用。当我 在取消引用 b1 和 b2 的情况下调用赋值运算符,程序首先查找在派生类上调用的函数的重写方法,如果找不到,则调用基方法类。现在,当我从 d1 调用赋值运算符时,程序将派生转换为基,并且程序无法知道 *d1 代表派生对象,因此它调用基方法。

这就是所有人。

【讨论】:

代码缺少理解它所需的类定义,以错误的方式获取输出行(大概,即通过推理和完成代码),并错过了关键点:什么你在这里展示的是“polymorphic”和“隐式使用c ++动态转换”的确切相反。如果这是多态的,那么*b1 = *b2 赋值将 报告它正在调用派生的operator=,但它不会(一旦我们纠正了您的消息是错误的方式)。 (试图让Base.operator=成为virtual的方法打开了一个不同的伤害世界)【参考方案3】:

除非我误解了您想要实现的目标,否则您需要一个 Derived 类的赋值运算符,即以 Derived 作为输入的运算符:

class Derived : public Base  
  
/* ... */
public:
    Derived& operator=(const Derived &rhs)  
      
        cout << "Assignment operator in Derived"<< endl;  
        return *this;  
    
;  

您的代码中发生了什么(已经在 Bo Persson 和 cmets 的回答中解释过): 在Derived 中,您实现了一个赋值运算符,它采用Base 的实例;但是在main() 中,您分配了Derived 的实例;编译器没有看到Derived 的赋值运算符(使用Base 的运算符不计算在内),因此它生成了一个,它调用Base::operator=(),然后为Derived 的数据成员赋值。如果你定义了如上所示的赋值,它就不会发生并且你的操作符会被调用;请注意,在这种情况下,Base 和数据成员的分配不会自动发生。


另一种情况是,如果您真的希望分配从 BaseDerived,例如将其与Base 的其他派生词一起使用。那么你定义的操作符就可以工作了,但是为了将它应用到Derived的一个实例上,你需要把这个实例转换为Base

Derived d1(10,20);  
Derived d2(30,40);  
d1 = static_cast<Base&>(d2);

不用说,您定义的运算符无法轻松访问特定于Derivedrhs 的数据成员:例如要使用rhs.y,您需要将rhs“向上转换”为Derived

Derived& Derived::operator=(const Base& rhs)

    /* ... */
    Derived* rhs_d = dynamic_cast<Derived*>(&rhs);
    if( rhs_d )
        this->y = rhs_d->y;

【讨论】:

【参考方案4】:

编译器为 Derived 生成一个默认的赋值运算符(隐藏了 Base 的运算符)。但是,默认赋值运算符会调用类成员和基类的所有赋值运算符。

【讨论】:

我可以将基类 operator= 重写为派生类吗? @Amit:你不能覆盖它。根据定义,一个非虚函数不能被覆盖,尽管它可以被隐藏。也许您想重载Derived 中的复制赋值运算符。如果是这样,那么正确的签名是Derived&amp; operator=(const Derived &amp;rhs)。这是编译器在您执行d1 = d2 时查找的签名。因为它没有找到它,所以它合成了默认的复制赋值运算符,并调用Base::operator=(const Base &amp;) @Steve:我已经更改了代码,现在我的基类 opertor= 是虚拟的。 @Amit:这几乎可以肯定是一个错误,虚拟赋值运算符不能很好地工作。您不想只允许将任何旧的 Base 对象分配给 Derived 对象,因为 Base 没有提供分配给 y 的值。【参考方案5】:

是的,只是基类 = 运算符被派生类 = 运算符隐藏了。

【讨论】:

我想知道:这种解释对我来说似乎不正确,声称我们会得到 Amit 预期的行为,但没有解释观察到的行为。当然,除非我完全误读了它。为什么要投票?

以上是关于派生类继承基类赋值运算符?的主要内容,如果未能解决你的问题,请参考以下文章

C++的探索路12继承与派生之高级篇--派生类与赋值运算符及多重继承

当我们复制/分配派生类对象时,如何在继承中复制基类成员?

如何从基类调用派生赋值运算符?

C++:在派生类构造函数中调用基类赋值运算符的错误形式?

包含指向派生模板类的基类指针的类的赋值运算符和复制构造函数

派生类在基类中删除时是不是会有隐式复制构造函数或赋值运算符?