CERT C++: OOP51-CPP

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

Описание

Управляйте определением

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

Примеры

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

Описание

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

Риск

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

  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 Carnegie Mellon University, веб-сайт SEI CERT-C © 2017 Carnegie Mellon University”, CERT SEI C Кодирование Стандарта – Правил для Разработки безопасных, Надежных и Защищенных систем – 2 016 Выпусков”, © 2016 Carnegie Mellon University, and “CERT SEI Стандарт Кодирования C++ – Правил для Разработки безопасных, Надежных и Защищенных систем на C++ – 2 016 Выпусков” © 2016 Carnegie Mellon University, со специальным разрешением от его Института программной инженерии.

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

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