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

类中动态数组问题:调用函数f()为何触发析构函数

问题分析与解答

调用f()时触发析构函数的原因

当你以值传递的方式将A类对象传入f(A a)时,C++会自动为函数参数a创建一个原对象(a1或a2)的副本。这个副本是函数f()内部的局部变量,当f()执行完毕,局部变量退出作用域,副本a被销毁,此时会调用它的析构函数~A()——这就是每次调用f()都会触发析构函数的原因。

数组相关问题的根源

你的A类使用了动态内存分配(new int[n]),但没有定义拷贝构造函数和拷贝赋值运算符。C++会使用默认版本,而默认版本只会执行浅拷贝:

  • 副本的p指针直接指向原对象p所指向的同一块堆内存;
  • 原对象和副本共享同一个数组。

当副本的析构函数在f()结束时执行,它会删除这块共享的数组内存,此时原对象的p就变成了悬空指针——指向已经被释放的内存。后续再使用原对象(比如再次调用f(),或者main()结束时原对象被销毁),会尝试访问或释放无效内存,导致未定义行为(崩溃、垃圾数据等),这就是数组无法正常工作的原因。

引用传递解决问题的原理

把函数改成int f(A &a)后,你是在按引用传递对象,不会创建原对象的副本。函数直接使用原对象本身,因此没有局部副本需要销毁,析构函数不会在函数调用时触发,原对象的动态内存也能保持完整。

彻底修复:遵循“三法则”

要从根本上解决问题(即使使用值传递也不会出错),你的类需要遵循三法则:既然自定义了析构函数,就必须同时定义拷贝构造函数和拷贝赋值运算符,实现数组的深拷贝。

修改后的类代码如下:

class A {
public:
    A(int i = 10) {
        n = i;
        p = new int[n];
        for (int j = 0; j < n; j++) {
            p[j] = 0;
        }
    }

    // 拷贝构造函数:深拷贝数组
    A(const A& other) {
        n = other.n;
        p = new int[n];
        for (int j = 0; j < n; j++) {
            p[j] = other.p[j];
        }
    }

    // 拷贝赋值运算符:深拷贝数组
    A& operator=(const A& other) {
        if (this != &other) { // 避免自赋值导致的错误
            delete[] p; // 先释放当前对象的内存
            n = other.n;
            p = new int[n];
            for (int j = 0; j < n; j++) {
                p[j] = other.p[j];
            }
        }
        return *this;
    }

    ~A() { delete[] p; } // 注意:数组必须用delete[]释放,不能用delete!
    int n;
    int* p;
};

另外注意:你原代码中的析构函数用了delete p,这是错误的!用new[]分配的数组必须用delete[]释放,否则会导致未定义行为。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.23 06:39:14