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

时间:2011-07-01 15:11:28

标签: c++ inheritance compiler-errors

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

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

为什么b = a;会抛出错误,而a = b;完全正常?

6 个答案:

答案 0 :(得分:62)

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

因此对于行b = a,只有operator=的{​​{1}}才是候选人。但是它的参数有B类型,不能用B const&参数初始化(你需要一个向下转换)。所以你得到一个错误。

答案 1 :(得分:24)

因为每个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,因此可以工作。但无论如何编译器都不会允许它。

答案 2 :(得分:6)

B确实是A,但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。请注意,A的赋值运算符只知道B中的数据,而不知道b = a; 中的数据,因此B的任何成员都不属于A丢失 - 这被称为'切片'。

但是当你试图分配时:

a

A的类型为B,不是a,因此B&无法将b参数与b=a匹配的任务操作员。

您会认为A& A::operator=(const A&)应该只调用继承的B& B::operator=(const B&),但事实并非如此。赋值运算符A隐藏了将从using A::operator=;继承的运算符。可以使用{{1}}声明再次恢复。

答案 3 :(得分:4)

我已经更改了结构的名称,使其显而易见:

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.另一方面,复制a到b可能会留下b不完整,因为B可能有比A更多的成员。这里很难看到因为A和B根本没有任何成员,但这就是编译器允许一个赋值而不允许另一个赋值的原因。

答案 4 :(得分:3)

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

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

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

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

a = b;没问题,因为B会与const A&的{​​{1}}参数匹配。由于只有A::operator=(const A&)的成员被“成员分配”到目标,因此A中不属于B的任何成员都会丢失 - 这称为“切片”。

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

struct B

B& B::operator=(const B&) 不正常,因为b = a;A参数不匹配。

答案 5 :(得分:1)

如果我接受采访,我将以一点哲学的方式解释。

a = b;

有效,因为每个B都包含A。因此a可以从A中提取B。但是,A不包含B。因此b无法在B内找到A;这就是原因,

b = a;

无效。

[类似地,void*可以找到Type*,但在Type*中找不到void*(因此我们需要演员)。]