как происходит нарезка производных классов? - PullRequest
2 голосов
/ 19 сентября 2019

У меня проблемы с пониманием того, как происходит нарезка?например, в этом фрагменте кода:

class A {
public:
  virtual h() {
    cout << "type A" << endl;
  }
};

class B : public A {
public:
  virtual h() override {
    cout << "type B" << endl;
  }
};

void f(A a) {
  a.h();
}

void g(A& a) {
  a.h();
}

int main() {
  A a1 = B(); // a1 is A and doesn't recognize any B properties
  A *a = new B();
  f(*a);
  g(*a);
}

я заметил, что:

  1. переменная a1 не знает, что это B, а переменная a знает.Я имею в виду, что это происходит потому, что в переменной a1 присваивание B происходит по значению, в отличие от переменной a, где я создаю указатель на B.

  2. То же самое происходит, когда я передаю переменнуюa к различным функциям - когда я передаю значение, оно думает, что это A, но когда я передаю ссылку, оно думает, что это B.

Я был бы счастлив, если бы кто-нибудь мог дать мнеболее обширное и глубокое объяснение.Заранее спасибо!

Ответы [ 2 ]

2 голосов
/ 19 сентября 2019
  1. переменная a1 не знает, что это B

Вернее: переменная a1 была объявлена ​​как A, так что это A.не B, и никогда не был B. Дело не в том, что переменная «знает»;речь идет о типе переменной.a1 был инициализирован из B путем «нарезки» копии базового подобъекта.

Я был бы рад, если бы кто-нибудь мог дать мне более подробное и более глубокое объяснение.

Направленность необходима для полиморфизма во время выполнения.Объект типа A всегда имеет тип A и не имеет другого типа.Это просто, как типы в языке.Одна из причин этого заключается в том, что компилятор должен знать, сколько памяти требуется объекту.Если компилятор резервирует память для A, то как потенциально больший производный экземпляр может поместиться в зарезервированную память?

Но указатель (или ссылка) на A может либо указывать на отдельный объект A, либо он может указыватьв базовом подобъекте класса, производного от A. Неважно, насколько велик весь объект, указатель может независимо указывать на часть объекта, и это не влияет на размер самого указателя.

как происходит разделение производных классов?

Разделение происходит всякий раз, когда вы преобразуете производный объект в базовый тип.Преобразование копирует подобъект базового класса.Обратите внимание, что когда вы преобразовываете производное значение в ссылку на базу, нарезка не происходит - если только вы не используете эту ссылку для инициализации объекта базового типа, например.

1 голос
/ 19 сентября 2019

В этом операторе

A a1 = B();

используется конструктор копирования по умолчанию класса A, поскольку объект a1 имеет тип A.

Этот конструктор выглядит как

constexpr A( const A & );

Таким образом, только подобъект типа A объекта класса B копируется в объект a1.Таблица указателей на виртуальные функции объекта a1 будет содержать указатель на собственную виртуальную функцию.

В этом объявлении

A *a = new B();

указатель a указывает на объекттип B, который содержит таблицу указателей на виртуальные функции, который в свою очередь содержит указатель на виртуальную функцию класса B.

Таким образом, в этом вызове

  g(*a);

ссылкак объекту динамического типа B передается (сам объект не был изменен, когда он был передан функции по ссылке).

Таким образом, внутри функции

void g(A& a) {
  a.h();
}.

будет доступ к таблице указателей виртуальных функций, которая содержит указатель на виртуальную функцию класса B. Здесь новый объект класса A не создается, поскольку исходный объект передается по ссылке.

В этом вызове

  f(*a);

аргумент функции передается по значению.То есть происходит инициализация копии параметра функции, объявленной как

void f(A a) {
  a.h();
}

Так что это фактически тот же случай, что и в объявлении

A a1 = B();

, рассмотренном выше.

...