You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何修复C++中对象切割引发的内存泄漏问题?

问题分析与解决方案

首先,原代码存在几个核心问题:

  • 基类Foo未定义虚赋值运算符,导致*f = *b执行时会发生对象切片,仅调用Foo的默认赋值运算符(浅拷贝),无法正确处理Bar类的成员。
  • Foo和Bar的默认构造函数未初始化指针成员为nullptr,析构时可能触发未定义行为。
  • 你重载的operator=逻辑不完整:既没有从other拷贝数据,也没有适配子类的多态场景,且j_使用的是当前对象的大小而非源对象的,无法实现真正的深拷贝。

正确实现方案

要实现*f = *b的多态深拷贝且避免内存泄漏,需要从以下几点入手:

1. 基类Foo添加虚赋值运算符

赋值运算符需要设为虚函数,确保通过基类指针调用时能触发子类的实现。同时完善深拷贝逻辑,释放原有内存后,正确拷贝源对象的数据。

2. 子类Bar重写赋值运算符

子类需要实现自己的赋值运算符,先处理基类部分的拷贝,再处理自身的成员变量,同时做类型检查保证安全。

完整修正代码:

#include <iostream>
#include <algorithm> // 用于std::copy

class Foo {
public:
    // 默认构造函数初始化指针为nullptr,避免析构时的未定义行为
    Foo() : i(nullptr), size_(0) {}
    Foo(int j) : size_(j) {
        i = new int[j](); // 初始化内存为0
    }

    // 虚析构函数(保留原逻辑)
    virtual ~Foo() {
        delete[] i;
    }

    // 虚赋值运算符,支持多态
    virtual Foo& operator=(const Foo& other) {
        if (this != &other) {
            // 先释放当前对象的内存
            delete[] i;
            // 拷贝源对象的大小和数据
            size_ = other.size_;
            i = new int[size_]();
            std::copy(other.i, other.i + size_, i);
        }
        return *this;
    }

protected:
    int* i;
    int size_; // 保存数组大小,用于深拷贝
};

class Bar : public Foo {
public:
    Bar(int j) : Foo(0) { // 基类部分初始化为空,因为Bar有独立的char数组
        bar_i = new char[j]();
        bar_size_ = j;
    }

    ~Bar() override {
        delete[] bar_i;
    }

    // 重写赋值运算符,处理子类成员
    Bar& operator=(const Foo& other) override {
        if (this != &other) {
            // 尝试将源对象转换为Bar类型,确保类型匹配
            const Bar* bar_other = dynamic_cast<const Bar*>(&other);
            if (bar_other) {
                // 释放当前子类的内存
                delete[] bar_i;
                // 重置基类部分的无效资源
                delete[] i;
                i = nullptr;
                size_ = 0;
                // 深拷贝Bar的成员数据
                bar_size_ = bar_other->bar_size_;
                bar_i = new char[bar_size_]();
                std::copy(bar_other->bar_i, bar_other->bar_i + bar_size_, bar_i);
            } else {
                // 源对象不是Bar类型,调用基类的赋值逻辑
                Foo::operator=(other);
            }
        }
        return *this;
    }

private:
    char* bar_i;
    int bar_size_;
};

int main() {
    int i = 5, j = 10;
    Foo* f = new Foo(i);
    Foo* b = new Bar(j);

    *f = *b; // 现在会调用Bar的operator=,实现正确深拷贝

    delete f;
    delete b;
}

关键细节说明

  • 虚赋值运算符:基类Foo的operator=设为virtual,子类Bar重写后,通过基类指针调用时会触发子类的实现,彻底避免对象切片问题。
  • 内存安全:赋值时先释放当前对象的原有内存,再分配新内存并拷贝数据,从根源避免内存泄漏。
  • 类型检查:在Bar的赋值运算符中使用dynamic_cast验证源对象类型,确保只有当源对象是Bar时才处理子类专属成员,否则回退到基类逻辑。
  • 初始化规范:所有构造函数都初始化指针成员为nullptr或分配内存时默认初始化,避免析构时的未定义行为。

内容的提问来源于stack exchange,提问作者mascai

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.13 13:27:02