ПроблемаСрез объекта происходит, когда вы передаете производный объект класса по значению в функцию, но функция ожидает, что объект базового класса будет параметром.
РискЕсли вы передаете производный объект класса по значению в функцию, вы ожидаете, что будет вызван конструктор производного класса. Если функция ожидает, что объект базового класса будет параметром:
Вызывается конструктор копирования базового класса.
В теле функции параметр рассматривается как объект базового класса.
На 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
квалификатор к параметру функции, как в предыдущем примере.