18

我在继承 operator= 时遇到问题。为什么这段代码不起作用,修复它的最佳方法是什么?

#include <iostream>

class A
{
public:
    A & operator=(const A & a)
    {
        x = a.x;
        return *this;
    }

    bool operator==(const A & a)
    {
        return x == a.x;
    }

    virtual int get() = 0; // Abstract

protected:
    int x;
};

class B : public A
{
public:
    B(int x)
    {
        this->x = x;
    }

    int get()
    {
        return x;
    }
};

class C : public A
{
public:
    C(int x)
    {
        this->x = x;
    }

    int get()
    {
        return x;
    }
};

int main()
{
    B b(3);
    C c(7);
    printf("B: %d C: %d B==C: %d\n", b.get(), c.get(), b==c);

    b = c; // compile error
    // error: no match for 'operator= in 'b = c'
    // note: candidates are B& B::operator=(const B&)

    printf("B: %d C: %d B==C: %d\n", b.get(), c.get(), b==c);
    return 0;
}
4

5 回答 5

35

如果你没有在一个类中声明复制赋值操作符,编译器会为你隐式声明一个。隐式声明的复制赋值运算符将隐藏任何继承的赋值运算符(阅读 C++ 中的“名称隐藏”),这意味着任何继承的赋值运算符将对非限定名称查找过程“不可见”(当你这样做时会发生这种情况b = c) ,除非您采取特定步骤“取消隐藏”它们。

在您的情况下,类B没有明确声明的复制赋值运算符。这意味着编译器将声明

B& B::operator =(const B&)

含蓄地。它将隐藏从A. 线

b = c;

不编译,因为这里唯一的候选者是上面隐式声明B::operator =的(编译器已经告诉过你了);所有其他候选人都被隐藏。并且由于c不可转换为B&,因此上述赋值无法编译。

如果你想让你的代码编译,你可以使用 using-declarationA::operator =通过添加来取消隐藏继承

using A::operator =;

给类的定义B。代码现在将编译,虽然它不是一个好的样式。您必须记住,在这种情况下,b = c分配将调用A::operator =,它仅分配所A涉及对象的部分。(但显然这是你的意图。)

或者,在这种情况下,您始终可以通过使用名称的限定版本来解决名称隐藏问题

b.A::operator =(c);
于 2010-10-07T14:08:41.887 回答
3

发生的事情是operator =编译器为任何没有的类生成的默认值是隐藏基类' operator =。在这种特殊情况下,编译器会const B &B::operator =(const B &)在幕后为您生成。您的分配与此运算符匹配,并完全忽略您在 中声明的那个class A。由于 aC&无法转换为 aB&编译器会生成您看到的错误。

你希望这发生,即使现在看起来很烦人。它会阻止您编写的代码工作。您不希望这样的代码工作,因为它允许将不相关的类型(B 和 C 具有共同的祖先,但继承中唯一重要的关系是父->子->孙关系,而不是兄弟关系)分配给一个其他。

从 ISA 的角度考虑它。是否应该仅仅因为他们都是a 就Car被允许分配给 a ?BoatVehicles

为了制作这样的作品,您应该使用信封/字母模式。信封(又名句柄)是一个专门的类,它的唯一工作是保存从特定基类(字母)派生的某个类的实例。句柄转发所有操作,但分配给包含的对象。对于赋值,它只是用从对象赋值的复制构造(使用“克隆”方法(又名虚拟构造函数))副本替换内部对象的实例。

于 2010-10-07T13:41:31.090 回答
1

您不能像这样跨层次分配 - B 和 C 是 A 的不同子类。您可以将 B 分配给 B 或将 C 分配给 C,但不能将 C 分配给 B,反之亦然。

您可能希望在 B 和 C 中实现,在尝试之前operator=将分配的 A 部分委托给。A::operator=否则,这些课程的 B 和 C 特定部分将在作业中丢失。

于 2010-10-07T13:42:16.160 回答
1

通常, operator= 在 B 中定义为

B& operator=(B const &);

由于 B 不是“C”的明确且可访问的基础,因此编译器不允许从 C 转换为 B。

如果您真的想将“C”分配给“B”,则“B”应支持适当的赋值运算符

B& operator=(C const &);
于 2010-10-07T13:55:48.850 回答
1

(可能不是修复,也可能不是你应该做的)但是......如果你真的必须的话,有一种方法可以强制解决这个问题:

 (A&)(*(&b)) = (A&)(*(&c))
于 2010-10-07T14:37:33.960 回答