Объектное разрезание

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

Описание

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

Риск

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

  1. Конструктор копии базового класса называется.

  2. В теле функции параметр рассматривается как объект базового класса.

На C++ методы virtual класса разрешены во время выполнения согласно фактическому типу объекта. Из-за объектного разрезания может быть названа неправильная реализация метода virtual. Например, базовый класс содержит метод virtual, и производный класс содержит реализацию того метода. Когда вы вызываете метод virtual от тела функции, метод базового класса называется, даже при том, что вы передаете объект производного класса функции.

Фиксация

Одна возможная фиксация должна передать объект ссылкой или указателем. Передача ссылкой или указателем не вызывает вызов конструкторов копии. Если вы не хотите, чтобы объект был изменен, используйте спецификатор const со своим параметром функции.

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

Примеры

развернуть все

#include <iostream>

class Base {
public:
    explicit Base(int b) {
    	_b = b;
    }
    virtual ~Base() {} 
    virtual int update() const;
protected:
    int _b;
};


class Derived: public Base {
public:
    explicit Derived(int b):Base(b) {}
    int update() const;
};

//Class methods definition

int Base::update() const {
    return (_b + 1);
}

int Derived::update() const {
    return (_b -1);
}


//Other function definitions
void funcPassByValue(const Base bObj) {
    std::cout << "Updated _b=" << bObj.update() << std::endl;
}

int main() {
    Derived dObj(0);
    funcPassByValue(dObj);       //Function call slices object
    return 0;
 }

В этом примере вызов funcPassByValue(dObj) приводит к выводу Updated _b=1 вместо ожидаемого Updated _b=-1. Поскольку funcPassByValue ожидает параметр объекта Base, он вызывает конструктора копии класса Base.

Поэтому даже при том, что вы передаете объект Derived dObj, функциональный funcPassByValue обрабатывает свой параметр b как объект Base. Это вызывает Base::update() вместо Derived::update().

Исправление — объект передачи ссылкой или указателем

Одно возможное исправление должно передать объект Derived dObj ссылкой или указателем. В следующем, исправленном примере funcPassByReference и funcPassByPointer имеют ту же цель как funcPassByValue в предыдущем примере. Однако funcPassByReference ожидает ссылку на объект Base, и funcPassByPointer ожидает указатель на объект Base.

При передаче объекта Derived d указателем или ссылкой не нарезает объект. Вызовы funcPassByReference(dObj) и funcPassByPointer(&dObj) производят ожидаемый результат Updated _b=-1.

#include <iostream>

class Base {
public:
    explicit Base(int b) {
    	_b = b;
    }
    virtual ~Base() {}
    virtual int update() const;
protected:
    int _b;
};


class Derived: public Base {
public:
    explicit Derived(int b):Base(b) {}
    int update() const;
};

//Class methods definition

int Base::update() const {
    return (_b + 1);
}

int Derived::update() const {
    return (_b -1);
}


//Other function definitions
void funcPassByReference(const Base& bRef) {
    std::cout << "Updated _b=" << bRef.update() << std::endl;
}

void funcPassByPointer(const Base* bPtr) {
    std::cout << "Updated _b=" << bPtr->update() << std::endl;
}

int main() {
    Derived dObj(0);
    funcPassByReference(dObj);       //Function call does not slice object
    funcPassByPointer(&dObj);       //Function call does not slice object
    return 0;
 }

Примечание

Если вы передаете значением, потому что копия объекта сделана, исходный объект не изменяется. Передача ссылкой или указателем делает объект уязвимым для модификации. Если вы обеспокоены своим изменяемым исходным объектом, добавьте спецификатор const в свой параметр функции, как в предыдущем примере.

Информация о результате

Группа: Объектно-ориентированный
Язык: C++
Значение по умолчанию: на
Синтаксис командной строки: OBJECT_SLICING
Влияние: высоко

Введенный в R2015b