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

如何强制std::vector使用移动构造函数而非复制构造函数?

解决GCC无法调用移动构造函数的问题

首先,咱们来分析你遇到的编译错误:error: use of deleted function ‘TestData::TestData(const TestData&)。这是因为你在add函数里调用vector.push_back(d)时,d是一个左值(所有命名的对象都是左值),而push_back的左值重载版本会尝试调用拷贝构造函数——但你已经显式删除了拷贝构造,所以编译器直接报错。

要强制编译器使用移动构造,你需要让push_back接收到一个右值(或者右值引用),这里有几种可行的方案:

方案1:用std::move将左值转为右值引用

修改add函数中的push_back调用,把d通过std::move转换成右值引用:

void add(std::vector<TestData> &vector) {
    TestData d;
    d.x = 1;
    d.ptr = new char[12];
    memcpy(d.ptr, "Test string", 11);
    vector.push_back(std::move(d)); // 这里用std::move触发移动构造
}

std::move并不会真的“移动”数据,它只是一个类型转换工具,告诉编译器:“这个对象的资源可以被转移,不用保留原状态”。这样push_back就会调用接受右值引用的重载,进而触发你的移动构造函数。

方案2:直接构造临时对象(自动成为右值)

你也可以直接在push_back参数里构造临时的TestData对象,临时对象本身就是右值,会自动触发移动构造:

void add(std::vector<TestData> &vector) {
    TestData d;
    d.x = 1;
    d.ptr = new char[12];
    memcpy(d.ptr, "Test string", 11);
    vector.push_back(std::move(d));
    // 或者直接构造临时对象(写法示例):
    // TestData temp;
    // temp.x = 1;
    // temp.ptr = new char[12];
    // memcpy(temp.ptr, "Test string", 11);
    // vector.push_back(std::move(temp));
}

关键修正:别用默认的移动构造!

你当前代码里用了=default的移动构造函数,这会导致双重释放的严重问题!默认的移动构造只是简单地逐成员拷贝值——对于指针ptr来说就是浅拷贝,移动之后原对象d的ptr仍然指向原来的内存地址。当add函数结束时,d被析构,会delete[] ptr;之后main函数结束时,vector里的TestData对象析构时又会delete[] ptr,这就造成了双重释放,程序必然崩溃。

所以你必须自己实现移动构造和移动赋值函数,把原对象的ptr置为nullptr:

struct TestData {
    TestData(const TestData&) = delete;
    TestData& operator=(const TestData&) = delete;

    // 自定义移动构造函数
    TestData(TestData &&other) noexcept
        : x(other.x), ptr(other.ptr) {
        other.ptr = nullptr; // 把原对象的指针置空,避免它析构时释放内存
        std::cout << "TestData(TestData &&)" << std::endl;
    }

    // 自定义移动赋值函数
    TestData& operator=(TestData &&other) noexcept {
        if (this != &other) {
            // 先释放当前对象的资源
            if (ptr != nullptr) delete[] ptr;
            // 转移资源
            x = other.x;
            ptr = other.ptr;
            other.ptr = nullptr;
        }
        std::cout << "TestData& operator=(TestData &&)" << std::endl;
        return *this;
    }

    TestData() { std::cout << "TestData()" << std::endl; }
    
    ~TestData() noexcept {
        if(ptr != nullptr) delete []ptr; // 修正:null不是C++关键字,改用nullptr
        std::cout << "~TestData(), ptr = " << (ptr == nullptr ? "nullptr" : "not null") << std::endl;
    }

    int x;
    char *ptr = nullptr;
};

这样移动之后,原对象的ptr变成nullptr,它的析构函数就不会释放内存,只有vector里的对象会负责释放资源,彻底避免了双重释放的问题。

另外,你析构函数里的if(ptr != null)是错误的,null不是C++的关键字,必须改成nullptr,否则也会编译报错。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.07 12:37:39