exponenta event banner

Нарезка объектов

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

Описание

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

Риск

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

  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 ожидает a Base параметр object, он вызывает 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