CERT C++: OOP51-CPP

Не срезать производные объекты

Описание

Определение правила

Не срезать производные объекты.[1]

Реализация Polyspace

Эта проверка проверяет наличие разрезов объектов.

Примеры

расширить все

Проблема

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

Риск

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

  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 квалификатор к параметру функции, как в предыдущем примере.

Проверяйте информацию

Группа: 09. Объектно-ориентированное программирование (OOP)
Введенный в R2019a

[1] Это программное обеспечение было создано MathWorks, включающее фрагменты: «Сайт SEI CERT-C», © 2017 Университет Карнеги Меллон, Веб-сайт SEI CERT-C + + © 2017 Университет Карнеги Меллон, "Стандарт кодирования SEI CERT C - Правила разработки безопасных, Надежные и безопасные системы - 2016 Edition ", © 2016 Университет Карнеги Меллон, и "Стандарт кодирования SEI CERT C++ - Правила разработки безопасных, Надежные и безопасные системы в C++ - 2016 Edition "© 2016 Университет Карнеги Меллон, с специального разрешения от его Института программной инженерии.

ЛЮБОЙ МАТЕРИАЛ УНИВЕРСИТЕТА КАРНЕГИ МЕЛЛОН И/ИЛИ ЕГО ИНЖЕНЕРНОГО ИНСТИТУТА ПРОГРАММНОГО ОБЕСПЕЧЕНИЯ, СОДЕРЖАЩИЙСЯ В НАСТОЯЩЕМ ДОКУМЕНТЕ, ПОСТАВЛЯЕТСЯ НА БАЗИСЕ «КАК ЕСТЬ». УНИВЕРСИТЕТ КАРНЕГИ МЕЛЛОН НЕ ДАЕТ НИКАКИХ ГАРАНТИЙ, ВЫРАЖЕННЫХ ИЛИ ПОДРАЗУМЕВАЕМЫХ, В ОТНОШЕНИИ ЛЮБОГО ВОПРОСА, ВКЛЮЧАЯ, НО НЕ ОГРАНИЧИВАЯСЬ, ГАРАНТИЮ ПРИГОДНОСТИ ДЛЯ ЦЕЛЕЙ ИЛИ КОММЕРЧЕСКОЙ ВЫГОДЫ, ИСКЛЮЧИТЕЛЬНОСТИ, ИЛИ УНИВЕРСИТЕТ КАРНЕГИ МЕЛЛОН НЕ ДАЕТ НИКАКИХ ГАРАНТИЙ В ОТНОШЕНИИ СВОБОДЫ ОТ ПАТЕНТА, ТОВАРНОГО ЗНАКА ИЛИ НАРУШЕНИЯ АВТОРСКИХ ПРАВ.

Это программное обеспечение и связанная с ним документация не были рассмотрены и не одобрены Университетом Карнеги-Меллон или его Институтом программной инженерии.