C++ 对象切片(Object Slicing)
对象切片是C++中的一个重要概念,指的是当派生类对象被赋值给基类对象时,派生类特有的部分会被"切掉",只保留基类的部分。
为什么会发生切片?
由于基类对象不知道派生类的存在,当把派生类对象赋值给基类对象时,编译器只会拷贝基类部分的数据,派生类独有的成员变量和方法都会被丢弃。
示例代码:
#include <iostream>
#include <vector>
using namespace std;
// 基类
class Animal {
public:
string name;
Animal(string n) : name(n) {}
virtual void speak() const {
cout << name << " says: I'm an animal" << endl;
}
};
// 派生类
class Dog : public Animal {
public:
string breed;
Dog(string n, string b) : Animal(n), breed(b) {}
void speak() const override {
cout << name << " says: Woof! I'm a " << breed << endl;
}
};
// 另一个派生类
class Cat : public Animal {
public:
int lives;
Cat(string n, int l) : Animal(n), lives(l) {}
void speak() const override {
cout << name << " says: Meow! I have " << lives << " lives" << endl;
}
};
int main() {
// 示例1:直接赋值导致的切片
Dog dog("Buddy", "Golden Retriever");
Animal animal = dog; // 对象切片发生在这里!
cout << "直接赋值导致的切片:" << endl;
animal.speak(); // 输出基类的方法,而不是Dog的speak()
cout << endl;
// 示例2:vector中存储对象导致的切片
vector<Animal> animals;
animals.push_back(Dog("Max", "Labrador")); // 切片!
animals.push_back(Cat("Whiskers", 9)); // 切片!
cout << "vector存储对象导致的切片:" << endl;
for (const auto& a : animals) {
a.speak(); // 都调用基类的speak(),多态失效!
}
cout << endl;
// 示例3:使用指针避免切片(正确的方式)
vector<Animal*> animalPtrs;
animalPtrs.push_back(new Dog("Rex", "German Shepherd"));
animalPtrs.push_back(new Cat("Mittens", 7));
cout << "使用指针避免切片(多态正常工作):" << endl;
for (const auto& ptr : animalPtrs) {
ptr->speak(); // 正确调用派生类的方法
}
cout << endl;
// 清理内存
for (auto ptr : animalPtrs) {
delete ptr;
}
return 0;
}
输出结果:
直接赋值导致的切片:
Buddy says: I'm an animal
vector存储对象导致的切片:
Max says: I'm an animal
Whiskers says: I'm an animal
使用指针避免切片(多态正常工作):
Rex says: Woof! I'm a German Shepherd
Mittens says: Meow! I have 7 lives
对象切片的具体表现
-
成员变量丢失:Dog的breed属性和Cat的lives属性被切掉
-
多态失效:虚函数表指针被覆盖,无法正确调用派生类的方法
-
类型信息丢失:所有对象都被视为基类类型
如何避免对象切片?
- 使用指针或引用
// 使用指针 - 保持多态性
vector<Animal*> animals;
animals.push_back(new Dog(...));
// 使用引用(需要确保对象生命周期)
void processAnimal(Animal& animal) {
animal.speak(); // 多态正常工作
}
- 使用智能指针(推荐)
#include <memory>
vector<unique_ptr<Animal>> animals;
animals.push_back(make_unique<Dog>("Buddy", "Golden Retriever"));
animals.push_back(make_unique<Cat>("Whiskers", 9));
- 使用std::reference_wrapper
std::reference_wrapper提供了一种包装引用的方式,使得引用可以像普通对象一样被复制、赋值,并存储在容器中。
#include <functional>
vector<reference_wrapper<Animal>> animals;
Dog dog("Buddy", "Golden Retriever");
Cat cat("Whiskers", 9);
animals.push_back(dog);
animals.push_back(cat);