Объектное Разрезание, это преимущество?

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

Class A{

}
Class B extends A{

}

Class SomeClass{
A a = new A();
B b = new B();

// Some where if might happen like this */
a = b; (Object slicing happens)

}

Мы говорим, что Объектное разрезание - кто-либо выгодный какими-либо способами? Если да, кто-либо может сказать мне, как разрезание объекта является полезным в разработке и где это могло бы быть полезно?

5
задан Moha the almighty camel 11 February 2014 в 11:00
поделиться

1 ответ

В C ++ срез объекта следует рассматривать как преобразование производного типа в базовый тип [* ]. Создается совершенно новый объект, «вдохновленный реальной историей».

Иногда это то, что вы хотели бы сделать, но результат ни в коем случае не совпадает с исходным. Когда нарезка объекта идет не так, как надо, это когда люди не обращают внимания и думают, что это тот же объект или его копия.

Обычно это не приносит пользы. Фактически, это обычно происходит случайно, когда кто-то передает по значению, когда они хотели передать по ссылке.

Довольно сложно придумать пример, когда нарезка - это определенно правильная вещь, потому что довольно сложно (особенно в C ++) придумать пример, в котором неабстрактный базовый класс определенно является правильным. делать. Это важный момент проектирования, и его нельзя игнорировать легкомысленно - если вы обнаружите, что нарезаете объект, намеренно или случайно, вполне вероятно, что ваша иерархия объектов неправильна с самого начала. Либо базовый класс не должен использоваться в качестве базового класса, либо он должен иметь хотя бы одну чистую виртуальную функцию и, следовательно, не может быть разрезан или передаваться по значению.

Итак, любой приведенный мной пример, где объект преобразуется в объект своего базового класса, справедливо вызвал бы возражение: «Погодите минутку, что вы делаете, в первую очередь, наследуя от конкретного класса?». Если нарезка происходит случайно, то, вероятно, это ошибка, а если она преднамеренная, то, вероятно, «запах кода».

Но ответ может быть таким: «Да, хорошо, это не должно действительно быть таким, как все устроено, но, учитывая, что они структурированы таким образом, мне нужно преобразовать из производный класс к базовому классу, и это по определению является срезом ". В этом духе вот пример:

struct Soldier {
    string name;
    string rank;
    string serialNumber;
};

struct ActiveSoldier : Soldier {
    string currentUnit;
    ActiveSoldier *commandingOfficer; // the design errors multiply!
    int yearsService;
};

template <typename InputIterator>
void takePrisoners(InputIterator first, InputIterator last) {
    while (first != last) {
        Soldier s(*first);
        // do some stuff with name, rank and serialNumber
       ++first;
    }
}

Теперь требование шаблона функции takePrisoners состоит в том, чтобы его параметр был итератором для типа, конвертируемого в Soldier.Это не обязательно должен быть производный класс, и мы не имеем прямого доступа к членам «name» и т. Д., Поэтому takePrisoners попытался предложить самый простой интерфейс для реализации с учетом ограничений (a) должен работать с Soldier, и (б) должна быть возможность писать другие типы, с которыми он также работает.

ActiveSoldier - еще один такой тип. По причинам, наиболее известным только автору этого класса, он решил публично наследовать от Soldier, а не предоставлять перегруженный оператор преобразования. Мы можем спорить, будет ли это когда-нибудь хорошей идеей, но предположим, что мы застряли на ней. Поскольку это производный класс, он может быть преобразован в Soldier. Это преобразование называется срезом. Следовательно, если мы вызовем takePrisoners , передав итераторы begin () и end () для вектора ActiveSoldiers, то мы разрежем их.

Вы, вероятно, могли бы придумать аналогичные примеры для OutputIterator, где получатель заботится только о части базового класса доставляемых объектов и, таким образом, позволяет их нарезать по мере их записи в итератор.

Причина, по которой это «запах кода», заключается в том, что мы должны рассмотреть (а) переписать ActiveSoldier и (б) изменить Soldier так, чтобы к нему можно было получить доступ с помощью функций вместо доступа к членам, чтобы мы могли абстрагироваться от этого набора функций как интерфейс, который другие типы могут реализовать независимо, так что takePrisoners не нужно преобразовывать в Soldier.Любой из них устранит необходимость в срезе и будет иметь потенциальные преимущества в плане простоты, с которой наш код может быть расширен в будущем.

[*], потому что это один. Последние две строки ниже делают то же самое:

struct A {
    int value;
    A(int v) : value(v) {}
};

struct B : A {
    int quantity;
    B(int v, int q) : A(v), quantity(q) {}
};

int main() {
    int i = 12;  // an integer
    B b(12, 3);  // an instance of B
    A a1 = b;    // (1) convert B to A, also known as "slicing"
    A a2 = i;    // (2) convert int to A, not known as "slicing"
}

Единственное отличие состоит в том, что (1) вызывает конструктор копирования A (который компилятор предоставляет, хотя код этого не делает), тогда как (2) вызывает конструктор A int.

Как кто-то сказал, Java не выполняет нарезку объектов. Если бы предоставленный вами код был преобразован в Java, то никакой нарезки объектов не произошло бы. Переменные Java - это ссылки, а не объекты,поэтому постусловие a = b просто состоит в том, что переменная «a» относится к тому же объекту, что и переменная «b» - изменения по одной ссылке можно увидеть по другой ссылке и так далее. Они просто ссылаются на него другим типом, который является частью полиморфизма. Типичная аналогия для этого состоит в том, что я могу думать о человеке как о «моем брате» [**], а кто-то другой может думать о том же человеке как о «моем викарии». Тот же объект, другой интерфейс.

Вы можете получить Java-подобный эффект в C ++, используя указатели или ссылки:

B b(24,7);
A *a3 = &b; // No slicing - a3 is a pointer to the object b
A &a4 = b;  // No slicing - a4 is a reference to (pseudonym for) the object b

[** ] На самом деле мой брат не викарий.

22
ответ дан 18 December 2019 в 07:08
поделиться