赋值运算符继承

30

有这样一段代码:

#include <iostream>

class Base {
public:
    Base(){
        std::cout << "Constructor base" << std::endl;
    }
    ~Base(){
        std::cout << "Destructor base" << std::endl;
    }
    Base& operator=(const Base& a){
        std::cout << "Assignment base" << std::endl;
    }
};

class Derived : public Base{
public:

};

int main ( int argc, char **argv ) {
    Derived p;
    Derived p2;
    p2 = p;
    return 0;
}

使用 g++ 4.6 编译后的输出结果:

Constructor base
Constructor base
Assignment base
Destructor base
Destructor base

尽管说赋值运算符不被继承,但为什么基类的赋值运算符会被调用?


1
可能是C++中operator=继承的问题的重复。 - Bo Persson
6个回答

49

实际上,被调用的是隐式定义的Derived类中的operator=。编译器提供的定义又会调用Base类中的operator=,因此你看到了相应的输出。构造函数和析构函数也是同理。当你让编译器定义operator=时,它会定义如下:

Derived& operator = (const Derived& rhs)
{
    Base1::operator =(rhs);
    ...
    Basen::operator =(rhs);
    member1 = rhs.member1;
    ...
    membern = rhs.membern; 
}

其中Base1,...,Basen是类的基类(按照在继承列表中指定它们的顺序),member1,...,membern是派生类Derived的成员(不包括继承的成员),按照你在类定义中声明它们的顺序。


在那个定义中加入一个return语句会很好,可能只需要return *this?如果Base1的运算符没有返回对自身的引用(虽然这通常是情况),那么它的行为可能会与Base1不同。 - Johannes

41

3
终于有人说出来了。+1。另请参见:https://dev59.com/zFHTa4cB1Zd3GeqPT7VP - underscore_d
1
如果您在派生类中声明附加变量,但使用此技术,会发生什么?它会正确地分配来自Derived的新变量吗? - ZeroZ30o

25

您没有默认值

Derived& operator=(const Base& a);

在您的Derived类中。

然而,会创建一个默认的赋值运算符:

Derived& operator=(const Derived& a);

这会调用Base的赋值运算符。因此,这不是继承赋值运算符的问题,而是通过派生类中默认生成的运算符来调用它。


11
根据1998年的C++标准,"operator="运算符函数和其他基类函数一样可以被继承。只不过对于派生类而言,会使用隐式创建的operator=运算符函数覆盖基类的同名函数。 - RoundPi
2
它刚被Derived类的隐式创建的operator=隐藏了。就像如果基类有void func(int),派生类有void func(double),那么基类函数void func(int)就会被隐藏。同样,基类operator=也会被派生类operator=隐藏。 - Sandeep
1
...并且要明确的是,由于基类operator=已经被隐藏,因此可以在派生类中使用using Base::operator=;将其取消隐藏,然后就可以继承了。参见:https://dev59.com/zFHTa4cB1Zd3GeqPT7VP - underscore_d
@Luchian:如果它没有被继承,就在基类中将其设为私有,并查看结果。 - Raindrop7

11

标准规定 (12.8):

赋值运算符应由一个参数的非静态成员函数实现。如果未由用户声明,则会为类隐式地声明拷贝赋值运算符 operator= (12.8),基类赋值运算符总是被派生类的拷贝赋值运算符隐藏。

然后派生类的赋值运算符调用您的基类

对于非联合类 X,隐式定义的拷贝/移动赋值运算符执行其子对象的逐成员拷贝/移动赋值。X 的直接基类首先按其在基类指定符列表中的声明顺序进行赋值,然后按照它们在类定义中声明的顺序赋值 X 的直接非静态数据成员。


-1

这是因为默认的赋值运算符会调用它的基础赋值运算符,即它并没有被继承,但仍然作为默认赋值运算符的一部分被调用。


赋值运算符是从基类继承的。它只是被派生类中由编译器生成的运算符隐藏了起来。使用 using Base::operator=; 可以通过取消隐藏基类实现并使其通过派生类可用。参见:https://dev59.com/zFHTa4cB1Zd3GeqPT7VP - underscore_d

-1

赋值运算符确实不会被继承。如果继承该运算符,您将能够将一个Base分配给一个Derived,但是Base b; p = a;将(正确地)无法编译。

发生的情况是编译器生成了一个operator=,因为您没有为Derived定义自定义的operator=。 自动生成的operator=将调用所有基类和所有成员的赋值运算符。在这方面,它与构造函数/析构函数非常相似,它们也调用所有Bases / members上的相应函数。


1
赋值运算符是从基类继承的。它只是被派生类中由编译器生成的运算符隐藏了起来。使用 using Base::operator=; 可以通过取消隐藏基类实现并使其通过派生类可用。参见:https://dev59.com/zFHTa4cB1Zd3GeqPT7VP - underscore_d

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接