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属性被切掉

  • 多态失效:虚函数表指针被覆盖,无法正确调用派生类的方法

  • 类型信息丢失:所有对象都被视为基类类型

如何避免对象切片?

  1. 使用指针或引用
// 使用指针 - 保持多态性
vector<Animal*> animals;
animals.push_back(new Dog(...));

// 使用引用(需要确保对象生命周期)
void processAnimal(Animal& animal) {
    animal.speak();  // 多态正常工作
}
  1. 使用智能指针(推荐)
#include <memory>
vector<unique_ptr<Animal>> animals;
animals.push_back(make_unique<Dog>("Buddy", "Golden Retriever"));
animals.push_back(make_unique<Cat>("Whiskers", 9));
  1. 使用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);
posted @ 2025-09-01 22:22  灰灰奋斗录  阅读(16)  评论(0)    收藏  举报