Почему допустимо присваивание базовому классу, а присвоение производному классу является ошибкой компиляции? - PullRequest
38 голосов
/ 01 июля 2011

Это был вопрос для интервью.Примите во внимание следующее:

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

Почему b = a; выдает ошибку, в то время как a = b; прекрасно?

Ответы [ 6 ]

62 голосов
/ 01 июля 2011

Поскольку неявно объявленный оператор присваивания копии B скрывает неявно объявленный оператор присваивания копии A.

Таким образом, для строки b = a только operator= из B является кандидатом.Но его параметр имеет тип B const&, который не может быть инициализирован аргументом A (вам понадобится downcast).Таким образом, вы получаете ошибку.

24 голосов
/ 01 июля 2011

Поскольку каждый 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;

Inв вашем примере нет атрибутов someInt и anotherInt, поэтому может работать.Но компилятор все равно этого не допустит.

6 голосов
/ 02 июля 2011

Это правда, что B - это A, но A - это не B, но этот факт непосредственно применим, только когда вы работаете с указателями или ссылками на A. и B х. Проблема здесь в вашем операторе присваивания.

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, поскольку B является A, поэтому 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=; декларации.

4 голосов
/ 01 июля 2011

Я изменил названия ваших структур, чтобы сделать причину очевидной:

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 вообще не имеют никаких членов, но именно поэтому компилятор допускает одно присваивание, а не другое.

3 голосов
/ 01 июля 2011

Помните, что если явно не объявлены операторы копирования-назначения, они будут неявно объявлены и определены для любого класса (а структуры - это классы в C ++).

Для struct A он будет иметь следующую подпись:

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

И он просто выполняет членское присваивание своих подобъектов.

a = b; в порядке, потому что B будет соответствовать параметру const A& для A::operator=(const A&).Поскольку только члены A назначаются для членов цели, все члены B, которые не являются частью A, теряются - это называется «нарезкой».

For struct B оператор присваивания implcit будет иметь следующую подпись:

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

b = a; не в порядке, поскольку A не будет соответствовать аргументу const B&.

1 голос
/ 01 июля 2011

Если я получу интервью, я объясню немного философски.

a = b;

действительно, потому что каждый B содержит A в качестве своей части.Таким образом, a может извлечь A изнутри B.Однако A не содержит B.таким образом b не может найти B изнутри A;поэтому

b = a;

недопустим.

[Аналогично, void* можно найти в любом Type*, но Type* нельзя найти в void* (таким образом,нам нужен актерский состав).]

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...