类中动态数组问题:调用函数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
相关产品推荐
相关产品推荐

