派生类是否间接继承了base的赋值运算符?

时间:2012-01-15 04:11:59

标签: c++ inheritance assignment-operator

我试图理解这种行为,但似乎我没有。请参阅此代码:

#include <iostream>
using namespace std;

class Base
{
public:
    void operator=(const Base& rf)
    {
        cout << "base operator=" << endl;
        this->y = rf.y;
    }
    int y;
    Base() : y(100) { }
};

class Derived : public Base
{
public:
    int x;
    Derived() : x(100) { }
};

int main()
{
    Derived test;
    Derived test2;
    test2.x = 0;
    test2.y = 0;
    test.operator=(test2); // operator auto-generated for derived class but...
    cout << test.x << endl << test.y << endl;
    cin.ignore();
    return 0;
}

计划输出:

> base operator=
>  0
>  0

现在我困惑的地方是: 规则说派生类永远不会继承分配运算符,而是创建自己的operator=,但是在这个示例中,基类operator=会在派生类上被调用。

第二,我能够在派生类上显式调用一个赋值运算符,而派生类在派生类中没有明确定义。

现在,如果我理解正确,这意味着任何用户定义的base的运算符总是在派生类上被调用?

4 个答案:

答案 0 :(得分:12)

生成的自动调用基类赋值运算符。

// generated version looks basically like this
Derived& operator=(Derived const& other){
  Base::operator=(static_cast<Base const&>(other));
  x = other.x;
  return *this;
}

演员在那里是为了避免对这样的模板Base::operator=进行一次致命的调用:

template<class Other>
Base& operator=(Other const& other); // accepts everything

或者像这样一个奇怪的人:

// forward-declare 'Derived' outside of 'Base'
Base& operator=(Derived const& other); // accepts derived class (for whatever reason)

  

第二,我能够在派生类上显式调用一个赋值运算符,而派生类在派生类中没有明确定义。

编译器会自动声明一个赋值运算符,如果你没有,你的类允许它(即没有引用成员和其他一些神秘规则),另外定义它你真的在某个地方使用它。

答案 1 :(得分:4)

编译器生成的赋值运算符调用每个子对象的赋值运算符。这包括基类和非静态成员变量。

标准说(第12.8节[class.copy]):

  

如果类定义没有显式声明一个复制赋值运算符,则会隐式声明一个。如果类定义声明了移动构造函数或移动赋值运算符,则隐式声明的复制赋值运算符被定义为已删除;否则,它被定义为默认值(8.4)。如果类具有用户声明的复制构造函数或用户声明的析构函数,则不推荐使用后一种情况。类X的隐式声明的复制赋值运算符将具有

形式
X&  X::operator=(const  X&)
     

如果

     
      
  • B的每个直接基类X都有一个副本赋值运算符,其参数类型为const B&,   const volatile B&B
  •   
  • 对于类X(或其数组)的M的所有非静态数据成员,每个此类类型都有一个复制赋值运算符,其参数类型为{{ 1}},const M&const volatile M&
  •   
     

否则,隐式声明的复制赋值运算符将具有

形式
M

  

非联合类X& X::operator=(X&) 的隐式定义的复制/移动赋值运算符执行其子对象的成员复制/移动分配。 X的直接基类首先按照它们在 base-specifier-list 中声明的顺序分配,然后分配X的直接非静态数据成员。按照在类定义中声明它们的顺序分配。设X为函数的参数   或者,对于移动运算符,引用参数的xvalue。每个子对象都以适合其类型的方式分配:

     
      
  • 如果子对象是类类型,就好是通过调用x将子对象作为对象表达式并将operator=的相应子对象作为单个函数参数(就像通过显式限定一样) ;也就是说,忽略更多派生类中任何可能的虚拟覆盖函数);
  •   
  • 如果子对象是一个数组,则以适合于元素类型的方式分配每个元素;
  •   
  • 如果子对象是标量类型,则使用内置赋值运算符。
  •   
     

未指定是否由隐式定义的复制赋值运算符多次分配表示虚拟基类的子对象。

答案 2 :(得分:2)

那是因为隐式定义的operator =调用基类operator =。请参阅FAQ lite:

I'm creating a derived class; should my assignment operator call my base class's assignment operator?

  

是(如果您需要首先定义赋值运算符)。

     

如果您定义自己的赋值运算符,编译器将不会自动为您调用基类的赋值运算符。除非您的基类的赋值运算符本身被破坏,否则应该从派生类的赋值运算符中显式调用它(同样,假设您首先创建一个)。

     

但是,如果您不创建自己的赋值运算符,则编译器为您创建的赋值运算符将自动调用基类的赋值运算符。

答案 3 :(得分:-1)

4件事永远不会被继承 构造函数 拷贝构造 分配操作员 析

即使您没有编写赋值运算符,您的代码也会很好。 每当使用赋值运算符时,编译器将确保调用每个成员对象和基类的赋值运算符。