为什么赋值给基类是有效的,但是赋值给派生类的编译错误?

这是一个面试问题。 考虑以下:

struct A {}; struct B : A {}; A a; B b; a = b; b = a; 

为什么b = a; 抛出一个错误,而a = b; 是完美的吗?

因为B的隐式声明的复制赋值运算符隐藏了B的隐式声明的复制赋值运算符。

所以对于b = a ,只有Boperator=是候选人。 但是它的参数有B const&B const& ,不能用A参数初始化(你需要向下)。 所以你得到一个错误。

因为每个B是一个A,但不是每个A都是一个B.

编辑下面的评论,使事情更清楚一点(我修改你的例子):

 struct A {int someInt;}; struct B : A {int anotherInt}; A a; B b; /* Compiler thinks: B inherits from A, so I'm going to create a new A from b, stripping B-specific fields. Then, I assign it to a. Let's do this! */ a = b; /* Compiler thinks: I'm missing some information here! If I create a new B from a, what do I put in b.anotherInt? Let's not do this! */ b = a; 

在你的例子中, someIntanotherInt没有属性,所以它可以工作。 但编译器不会允许它。

确实, BA ,但A不是B ,但是这个事实只有在你使用指针或者引用AB的时候才是直接适用的。 这里的问题是你的赋值操作符。

 struct A {}; struct B : A {}; 

相当于

 struct A { A& operator=(const A&); }; struct B : A { B& operator=(const B&); }; 

所以当你在下面分配时:

 A a; B b; a = b; 

a上的赋值运算符可以用b的参数来调用,因为BA ,所以b可以作为A&被传递给赋值运算符。 请注意,赋值操作符只知道A的数据,而不知道B ,所以B中不属于A的任何成员都会丢失 – 这就是所谓的“分片”。

但是当你试图分配:

 b = a; 

a是typesA ,它不是B ,所以a不能和B&的赋值运算符相匹配。

你会认为b=a应该调用inheritance的A& A::operator=(const A&) ,但事实并非如此。 赋值运算符B& B::operator=(const B&)隐藏了将从Ainheritance的运算符。 它可以using A::operator=;来重新恢复using A::operator=; 宣言。

我已经改变了你的结构的名字,使原因很明显:

 struct Animal {}; struct Bear : Animal {}; Animal a; Bear b; a = b; // line 1 b = a; // line 2 

显然,任何熊也是一种动物,但不是每一只动物都可以被认为是一只熊。

因为每个B“isa”A,B的任何实例也必须是A的一个实例:根据定义,它具有与A的任何其他实例相同顺序的相同成员。将b复制到a中会丢失特定于B的成员,但完全填充满足A的要求的结构的成员。另一方面,将a复制到b可能会使b不完整,因为B可能具有比A更多的成员。在这里很难看到,因为A和B都不是有任何成员,但这就是为什么编译器允许一个分配,而不是其他。

请记住,如果没有显式声明的复制赋值操作符,将隐式声明和定义任何类(并且结构是C ++中的类)。

对于struct A它将具有以下签名:

 A& A::operator=(const A&) 

它只是执行其子对象的成员分配。

a = b; 是可以的,因为B将与A::operator=(const A&)const A&参数相匹配。 由于只有A的成员被“成员分配”给目标,所以B中不属于A任何成员都会丢失 – 这就是所谓的“切片”。

对于struct B ,implcit赋值运算符将具有以下签名:

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

b = a; 不正确,因为A不会匹配const B&参数。

如果我接受采访的话,我会用一点哲学的方式来解释。

 a = b; 

是有效的,因为每个B包含A作为其一部分。 所以可以从B提取A 但是, A不包含B 因此bA内找不到B ; 这就是为什么,

 b = a; 

是无效的。

[类似地, void*可以在任何Type*find,但Type*不能在void*find(因此我们需要一个cast)。]