C++对象切割问题
对象切割(Object Slicing)是 C++ 中的一个常见问题,发生在对象拷贝时,尤其是在涉及基类与派生类之间的赋值或传递时。其结果是派生类对象被转换为基类对象时,丢失了派生类中的特定信息或成员,导致派生类的行为不能被完整地保留。
何时会发生对象切割
对象切割通常在以下几种场景中发生:
-
值传递或赋值:当派生类对象通过值传递或赋值给基类对象时,基类只会保留它自己的部分,派生类特有的部分将会被“切割掉”。
-
通过值捕获异常:在异常处理时,如果通过值捕获基类的异常对象而实际抛出的是派生类对象,也会发生对象切割。
因此根本原因是错误的使用了多态。
class Base {
public:
virtual void show() const {
std::cout << "Base class show()" << std::endl;
}
};
class Derived : public Base {
public:
int derivedData = 100;
void show() const override {
std::cout << "Derived class show(), derivedData = " << derivedData << std::endl;
}
};
void passByValue(Base b) {
b.show(); // 这里发生对象切割
}
int main() {
Derived d;
passByValue(d); // 将 Derived 对象传递给接受 Base 参数的函数
return 0;
}
输出:
Base class show()
-
在
passByValue函数中,派生类Derived对象d被按值传递给Base参数b。此时,b是一个Base类型的副本,因此只保留了Base类中的部分,Derived类中的derivedData以及派生类的show()实现都被“切割掉”了。 -
因此,输出的是基类的
show()方法,而非Derived类的show()。
解决方法:
避免对象切割的方法主要有两种:
-
使用指针或引用:避免按值传递对象,而是传递对象的指针或引用。这可以保留派生类的所有行为和属性。
-
使用多态性:通过使用指针或引用传递对象,C++ 的运行时类型信息(RTTI)能够保证派生类对象可以正常调用其派生类的方法。
class Base {
public:
virtual void show() const {
std::cout << "Base class show()" << std::endl;
}
};
class Derived : public Base {
public:
int derivedData = 100;
void show() const override {
std::cout << "Derived class show(), derivedData = " << derivedData << std::endl;
}
};
void passByReference(const Base& b) {
b.show(); // 通过引用避免对象切割
}
int main() {
Derived d;
passByReference(d); // 传递派生类对象的引用
return 0;
}
输出:
Derived class show(), derivedData = 100

浙公网安备 33010602011771号