Большинство ответов здесь не могут объяснить, какова фактическая проблема с разрезанием. Они объясняют только доброкачественные случаи нарезки, а не предательские. Предположим, как и другие ответы, что вы имеете дело с двумя классами A
и B
, где B
получает (публично) из A
.
В этой ситуации C ++ позволяет вам передать экземпляр оператора B
в A
, а также в конструктор копирования. Это работает, потому что экземпляр B
может быть преобразован в const A&
, а именно, какие операторы присваивания и конструкторы-копии ожидают, что их аргументы будут.
B b;
A a = b;
Ничего плохого в этом нет - вы попросили экземпляр A
, который является копией B
, и это именно то, что вы получаете. Конечно, a
не будет содержать некоторых членов b
, но как это сделать? В конце концов, это A
, а не B
, поэтому даже не слышал об этих членах, не говоря уже о возможности их хранения.
B b1;
B b2;
A& a_ref = b2;
a_ref = b1;
//b2 now contains a mixture of b1 and b2!
Вы можете подумать, что b2
будет копией b1
впоследствии. Но, увы, это не так! Если вы проверите его, вы обнаружите, что b2
- это франкенштейновское существо, сделанное из некоторых кусков b1
(куски, которые B
наследует от A
), и некоторые фрагменты b2
(куски что только B
содержит). Ой!
Что случилось? Ну, C ++ по умолчанию не обрабатывает операторы присваивания как virtual
. Таким образом, строка a_ref = b1
вызовет оператор присваивания A
, а не номер B
. Это связано с тем, что для не виртуальных функций объявленный тип (который является A&
) определяет, какая функция вызывается, в отличие от фактического типа (который был бы B
, поскольку a_ref
ссылается на экземпляр B
). Теперь оператор присваивания A
явно знает только о членах, объявленных в A
, поэтому он будет копировать только те, оставляя члены, добавленные в B
без изменений.
Присвоение только части объекта обычно имеет мало смысла, но, к сожалению, C ++ не предусматривает встроенного способа запретить это. Вы можете, однако, бросить свои собственные. Первый шаг - сделать оператор присваивания виртуальным . Это гарантирует, что он всегда является оператором присваивания фактического типа, который вызывается, а не объявленным типом. Второй шаг - использовать dynamic_cast
, чтобы убедиться, что назначенный объект имеет совместимый тип. Третий шаг - выполнить фактическое присвоение в члене (protected!) assign()
, так как B
assign()
, вероятно, захочет использовать A
's assign()
для копирования A
членов .
class A {
public:
virtual A& operator= (const A& a) {
assign(a);
return *this;
}
protected:
void assign(const A& a) {
// copy members of A from a to this
}
};
class B : public A {
public:
virtual B& operator= (const A& a) {
if (const B* b = dynamic_cast<const B*>(&a))
assign(*b);
else
throw bad_assignment();
return *this;
}
protected:
void assign(const B& b) {
A::assign(b); // Let A's assign() copy members of A from b to this
// copy members of B from b to this
}
};
Обратите внимание, что для чистого удобства B
operator=
ковариантно переопределяет возвращаемый тип, поскольку он знает, что он возвращает экземпляр B
.