C++ 中运算符 = 继承的问题
我在继承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;
}
I'm having trouble with the inheritance of operator=. Why doesn't this code work, and what is the best way to fix it?
#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;
}
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(5)
如果您没有在类中声明复制赋值运算符,编译器将隐式为您声明一个。隐式声明的复制赋值运算符将隐藏任何继承的赋值运算符(请阅读 C++ 中的“名称隐藏”),这意味着任何继承的赋值运算符对于不合格的都将变得“不可见”。 em> 名称查找过程(这就是当您执行
b = c
时发生的情况),除非您采取特定步骤“取消隐藏”它们。在您的情况下,类
B
没有显式声明的复制赋值运算符。这意味着编译器将隐式声明。它将隐藏从
A
继承的运算符。该行无法编译,因为,这里唯一的候选者是上面隐式声明的
B::operator =
(编译器已经告诉你了);所有其他候选人均被隐藏。由于c
无法转换为B&
,因此上述赋值无法编译。如果您希望代码能够编译,可以使用 using-declaration 通过添加
到类
B
的定义来取消隐藏继承的A::operator =
。代码现在可以编译了,尽管它不是一个好的风格。您必须记住,在这种情况下,b = c
赋值将调用A::operator =
,它仅分配A
部分所涉及的对象。 (但显然这就是您的意图。)或者,在这种情况下,您始终可以通过使用名称的限定版本来解决名称隐藏问题
If you do not declare copy-assignment operator in a class, the compiler will declare one for you implicitly. The implicitly declared copy-assignment operator will hide any inherited assignment operators (read about "name hiding" in C++), meaning that any inherited assignment operators will become "invisible" to the unqualified name lookup process (which is what happens when you do
b = c
), unless you take specific steps to "unhide" them.In your case, class
B
has no explicitly declared copy-assignment operator. Which mean that the compiler will declareimplicitly. It will hide the operator inherited from
A
. The linedoes not compile, because, the only candidate here is the above implicitly declared
B::operator =
(the compiler told you about that already); all other candidates are hidden. And sincec
is not convertible toB&
, the above assignment does not compile.If you want your code to compile, you can use using-declaration to unhide the inherited
A::operator =
by addingto the definition of class
B
. The code will now compile, although it won't be a good style. You have to keep in mind that in this case theb = c
assignment will invokeA::operator =
, which assigns only theA
portions of the objects involved. (But apparently that is your intent.)Alternatively, in cases like this you can always work around name hiding by using a qualified version of the name
发生的情况是,编译器为任何没有运算符的类生成的默认
operator =
隐藏了基类的operator =
。在这种特殊情况下,编译器会在幕后为您生成const B &B::operator =(const B &)
。您的赋值与此运算符匹配,并完全忽略您在class A
中声明的运算符。由于C&
无法转换为B&
,编译器会生成您看到的错误。你希望这一切发生,尽管现在看起来很令人烦恼。它会阻止您编写的代码运行。您不希望这样的代码起作用,因为它允许分配不相关的类型(B 和 C 有一个共同的祖先,但继承中唯一重要的关系是父->子->孙子关系,而不是兄弟关系)彼此。
从 ISA 的角度来考虑。是否应该允许将
汽车
分配给船
,仅仅因为它们都是车辆
?为了使类似的功能有效,您应该使用 Envelope/Letter 模式。信封(又名句柄)是一个专门的类,其唯一的工作是保存从特定基类(字母)派生的某个类的实例。句柄转发除对所包含对象的赋值之外的所有操作。对于赋值,它只是将内部对象的实例替换为从对象分配的复制构造(使用“克隆”方法(也称为虚拟构造函数))副本。
What's happening is that the default
operator =
that the compiler generates for any class that doesn't have one is hiding the base class'operator =
. In this particular case, the compiler is generatingconst B &B::operator =(const B &)
for you behind the scenes. Your assignment matches this operator and completely ignores the one you declared inclass A
. Since aC&
cannot be converted to aB&
the compiler generates the error you see.You want this to happen, even though it seems vexing right now. It prevents code like you've written from working. You don't want code like that to work because it allows unrelated types (B and C have a common ancestor, but the only important relationships in inheritance are parent->child->grandchild relationships, not sibling relationships) to be assigned to one another.
Think about it from an ISA perspective. Should a
Car
be allowed to be assigned to aBoat
just because they're bothVehicles
?In order make something like this work you should use the Envelope/Letter pattern. The envelope (aka handle) is a specialized class who's only job it is is to hold an instance of some class that's derived from a particular base class (the letter). The handle forwards all operations but assignment to the contained object. For assignment it simply replaces the instance of the internal object with a copy-constructed (using a 'clone' method (aka virtual constructor)) copy of the assigned from object.
您不能像这样跨层次结构分配 - B 和 C 是 A 的不同子类。您可以将 B 分配给 B 或将 C 分配给 C,但不能将 C 分配给 B,反之亦然。
您可能希望在 B 和 C 中实现
operator=
,在尝试此操作之前将赋值的 A 部分委托给A::operator=
。否则,这些课程的 B 和 C 特定部分将在作业中丢失。You cannot assign across the hierarchy like this - B and C are different subclasses of A. You can assign a B to a B or a C to a C but not a C to a B or vice versa.
You probably want to implement
operator=
in B and C, delegating the A part of the assignment toA::operator=
before you try this though. Otherwise the B- and C-specific parts of those classes will get lost in the assignment.通常,operator= 在 B 中定义为
由于 B 不是“C”的明确且可访问的基数,因此编译器不允许从 C 到 B 的转换。
如果您确实希望将“C”分配给“B”,则“B”应该支持适当的赋值运算符,如下所示
Normally, operator= is defined in B as
Since B is not an unambiguous and accessible base of 'C', the conversion from C to B is not allowed by the compiler.
If you really want to have a 'C' be assigned to 'B', 'B' should support an appropriate assignment operator as
(可能不是解决办法,也可能不是你应该做的)但是......如果你真的必须这样做,有一种方法可以强制解决这个问题:
(Probably not a fix & probably not what you should do) BUT... there's a way you can force the issue if you really must: