Object slicing

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

Описание

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

Риск

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

  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++
По умолчанию: On для рукописного кода, off для сгенерированного кода
Синтаксис командной строки: OBJECT_SLICING
Влияние: Высокий
Введенный в R2015b