子类、赋值运算符重载?

6
如果我有以下类:
class TestA
{
    public:
        const TestA &operator=(const int A){return *this;}
};

class TestB : public TestA
{
    public:
        //Inheritance?
};

这个问题假设TestA类和TestB类在变量方面完全相同:赋值运算符(或任何其他运算符)是否被继承?
以下写法是否正确?
class TestB : public TestA
{
    public:
        using TestA::operator=;
        //Inheritance?
};

如果它是有效的,它会有所不同吗?

作为一个注释:operator= 应当返回一个非const引用 this - king_nak
@king_nak 它可以返回任何它想要的东西 -- 甚至是 void(但是如果你想像 a = b = c 那样链接赋值操作,它应该返回非 const 引用)。 - Alexander Poluektov
@Alex 如果必须使用const,返回const对于链式操作没有影响,因为它是从右到左的。 - SE Does Not Like Dissent
@Alex & Sight 作为重载运算符,应该像 POD 一样运行,返回值应该是一个非 const 引用。使用 const 引用可以进行链式操作,但不支持语句 (a = b) = c;,这在 POD 上是有效的。但这只是一个非常特殊的情况... - king_nak
@king_nak “道德上”它应该像POD一样运作,但“法律上”它可以返回任何东西。我谈论的是“合法性”问题。从“道德”角度来看,它也应该接受不是int而是const ref参数。 - Alexander Poluektov
@Alex 在其他情况下,某些参数在道义上也是可以接受的(例如 int 也可以分配给 float...)。例如一个时间包装类,在其中你可以像 time() 返回的 long 一样进行赋值。但一般来说,黄金法则是不要惊讶你的类用户并做出意外的事情。 - king_nak
4个回答

3

默认情况下,派生类会隐藏赋值运算符(因为编译器总是为任何class T生成一个T& operator =(),如果没有指定的话)。这使得继承的赋值运算符不能使用。

是的,当你使用using关键字指定它们后,它们就变得可用了。示例。所以你的代码片段是有意义的。

public: using TestA::operator=;

1
使用 using 指定的方法具有放置 using 的可见性。因此,如果它在 public: 之前,它们就是私有的...通过这种方式,您可以更改继承方法的可见性。 - king_nak
1
这正是我所期望的!谢谢iammilind! - SE Does Not Like Dissent
@king_nak,是的,你说得对。我之前一直认为usingfriend是一样的,可以放在任何限定符前面。 - iammilind
+1(几乎),由于答案已经修复,我删除了踩一下。实际上,当我写这个评论的时候,我忘记踩一下了,所以现在的行动其实什么都没有做。但是这只是一种态度。 - Cheers and hth. - Alf
@Alf,精神就是当你真正点赞而不仅仅是几乎点赞的时候。 ;) - iammilind
显示剩余2条评论

3

我在写这篇文章时,问题的示例代码如下:

class TestA
{
    public:
        const TestA &operator=(const int A){return *this;}
};

class TestB : public TestA
{
    public:
        //Inheritance?
};

Q1: “赋值运算符(或其他运算符)是否会被继承?”

是的,当然会。在 C++98 中,唯一不会被继承的成员函数是构造函数。然而,基类实现默认被自动生成的复制赋值运算符隐藏。例如:

#include <iostream>
using namespace std;

class TestA
{
    public:
        TestA const& operator=( int const )
        {
            cout << "TestA = int" << endl;
            return *this;
        }
};

class TestB : public TestA
{
    public:
        // Automatically generated copy assignment operator.
};

int main()
{
    TestB   o;

    cout << "Calling automatically generated copy assignment:" << endl;
    cout << "(should be nothing here ->) ";  o = TestB();
    cout << endl;
    cout << endl;

    cout << "Calling base class assignment operator:" << endl;
    // o = 42;     // won't compile, because it's hidden.
    cout << "(should be output here ->) ";  o.TestA::operator=( 42 );   // OK.
    cout << endl;
}

考虑到目前为止所有的答案都混淆了隐藏和继承的概念,为了在这里澄清术语,N3290(与C++11标准相同)指出:

N3290 §10/2:
"继承成员可以像派生类的其他成员一样在表达式中引用,除非它们的名称被隐藏或存在歧义"

在上面的示例代码中,TestA::operator= 被隐藏了,但请参见问题3的答案。

Q2:以下代码是否有效?

此问题涉及在派生类中使用 using,例如:

class TestB : public TestA
{
    public:
        using TestA::operator=;
        //Inheritance?
};

是的,这是有效的。

对于构造函数来说,在C++98中它是无效的,因为构造函数不会被继承。

问题3: 如果它是有效的,是否有区别?

是的,它会使基类的赋值运算符在派生类中直接可访问。

例如,您可以在上面的示例中取消注释,

// o = 42;     // won't compile, because it's hidden.

并且它仍然可以编译通过,

o = 42;        // compiles fine with "using".

干杯并祝一切顺利。

Alf现在得到了正确的答案,但我仍然对我引用的标准中的最后一句话感到有些困惑:“由using-declaration引入的运算符被派生类中的隐式声明运算符所隐藏。”请参见我上面的回答获取更多细节。 - RoundPi
感谢您提供更详细的答案,我们非常感激这些信息。 - SE Does Not Like Dissent
@Alf:Q2,同意,但我建议尽可能避免使用它,因为a)对于切片,即使现在没有切片;b)它不能按顺序使用,例如TestB t1, t2; t1 = t2 = 0; - Andriy Tylychko

2
我从C++ 11标准中找到了一些相关的答案: 12.8 复制和移动类对象 由于如果用户没有声明,那么一个类将隐式地声明一个复制/移动赋值运算符,所以一个基类的复制/移动赋值运算符总是被派生类的对应赋值运算符所隐藏(13.5.3)。从基类引入一个赋值运算符的using-declaration(7.3.3),其参数类型可能是派生类的复制/移动赋值运算符的参数类型,不被认为是这样一个运算符的显式声明,并且不会抑制派生类运算符的隐式声明;由using-declaration引入的运算符被派生类中的隐式声明的运算符所隐藏。

谢谢。我通常喜欢在Stack Overflow上听取意见,因为它们提供了我通常不会考虑到的见解和考虑因素。 - SE Does Not Like Dissent
1
赋值运算符只是一个(特殊的)成员函数,并且是继承的。然而,默认情况下,它被自动生成的一个隐藏了。 - Cheers and hth. - Alf
然而,我对标准中的最后一句话有些困惑:由 using-declaration 引入的运算符会被派生类中隐式声明的运算符所隐藏。 - RoundPi

0

赋值运算符不被派生类继承。如果你在派生类中使用了你重载的赋值运算符,从你的基类中,你可能会遇到对象切割问题:sizeof(base)!= sizeof(derived)


1
我相信即使 sizeof(base) != sizeof(derived),它仍然被称为“切片”。 - Alexander Poluektov
1
@SSight:每当传入的对象类型是从base派生出来并且具有自己的数据成员时,你都会遇到切片操作。 - R. Martinho Fernandes
1
即使派生类中没有新的数据成员,仍然会发生切片:派生类对象被“切片”,并返回基类的子对象。如果基类有虚函数而派生类覆盖了它们,那么这将是灾难性的。 - Alexander Poluektov
@Alex 这是我在 Stack Overflow 上发布问题的原因之一。根据你的说法,我可能需要开始另一个问题。 - SE Does Not Like Dissent
@Fernanders 这是关于切片以及它是否与派生类中的附加成员变量相关的一般性陈述。对于造成的困惑,我们感到抱歉。 - Alexander Poluektov
显示剩余5条评论

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