Игнорировать конкретные звонки в шутливом освещении

Большинство ответов здесь не могут объяснить, какова фактическая проблема с разрезанием. Они объясняют только доброкачественные случаи нарезки, а не предательские. Предположим, как и другие ответы, что вы имеете дело с двумя классами 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.

0
задан Juan Rivas 24 March 2019 в 16:27
поделиться