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

C++析构函数调用次数异常咨询:为何出现3次析构调用?

问题分析:额外析构调用的真相

你的代码中总共存在3个A类型对象,对应3次析构调用,输出3个0,加上两次拷贝构造的1,最终输出11000是完全正常的,并没有“额外”的析构。你误以为析构来自那一行,是因为对析构时机的误解,加上代码中的一个小错误导致的混淆:

1. 对象数量与析构时机拆解

  • 栈对象a:在main函数开始时构造,main执行完毕返回时自动析构,输出第一个0。
  • 堆对象*b:通过new A(a)构造,触发拷贝构造函数输出1,在delete b时析构,输出第二个0。
  • 堆对象*c:通过new A(*b)构造,触发拷贝构造函数输出第二个1,在delete c时析构,输出第三个0。

你看到的“那一行输出110”,实际是那一行执行时输出两个1,紧接着main函数返回时栈对象a析构输出0,你误以为这个0是那一行触发的。

2. 拷贝构造函数的错误

你的拷贝构造函数中使用A::v = a.v;是错误的:

A(A &a) {A::v = a.v; cout << "1";}

v是类的非静态成员,不能通过类名::成员名的方式直接访问(这种写法仅适用于静态成员)。正确的写法应该是直接赋值给当前对象的v:

A(A &a) {v = a.v; cout << "1";}
// 或者显式使用this指针:this->v = a.v;

这个错误属于未定义行为,编译器可能生成异常代码,加重了你对析构来源的误解,但即使修正这个错误,最终输出依然是11000,因为3个对象的析构是必然的。

3. 验证方式

你可以在构造函数和析构函数中添加对象地址的输出,清晰看到每个对象的生命周期:

#include <iostream>
using namespace std;

class A {
public:
    float v;
    A() {v = 1.0; cout << "[构造] 栈对象: " << this << endl;}
    A(A &a) {v = a.v; cout << "[拷贝构造] 堆对象: " << this << " 来自: " << &a << endl;}
    ~A() { cout << "[析构] 对象: " << this << endl;}
    float set(float v){
        this->v = v;
        return v;
    }
    float get(float v){
        return this->v;
    }
};

int main(){
    cout << "=== 创建对象 ===" << endl;
    A a, *b = new A(a), *c = new A(*b);
    cout << "=== 执行函数调用 ===" << endl;
    c->get(b->get(a.set(1.0)));
    cout << "=== 删除堆对象b ===" << endl;
    delete b;
    cout << "=== 删除堆对象c ===" << endl;
    delete c;
    cout << "=== main函数结束 ===" << endl;
    return 0;
}

运行后你会清晰看到:

  • 栈对象a的析构发生在main函数结束时,而非对象创建的那一行。
  • 两次delete分别对应两个堆对象的析构。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.04 12:04:57