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

如何在C++的for循环中创建独立对象及内存管理问题

关于std::unique_ptr存入vector的问题及C++内存管理实践

一、解决unique_ptr存入vector的两个问题

1. 拷贝构造被删除的警告

std::unique_ptr是独占式智能指针,设计初衷就是确保同一时间只有一个指针持有原始内存的所有权,因此它的拷贝构造函数和拷贝赋值运算符被显式删除了——这就是你看到call to implicitly-deleted copy constructor警告的原因。

要把unique_ptr存入vector,必须用std::move()转移所有权:

  • std::move()会把原unique_ptr的所有权转移给vector中的新元素
  • 转移后,原unique_ptr会被置为空指针,不再持有任何内存

2. 遍历打印地址均相同的问题

这个问题大概率是你把栈上局部对象的地址交给了unique_ptr。栈上的局部变量在循环迭代结束后会被销毁,下一次迭代时栈空间会被复用,所以新对象的地址和上一次完全相同。更严重的是:unique_ptr在析构时会尝试delete这个栈地址,这属于未定义行为(栈内存不能被delete)。

正确的做法是在堆上创建对象,每次都会分配新的内存地址,避免栈空间复用的问题。

正确代码示例

#include <vector>
#include <memory>
#include <iostream>

struct SomeObject {};

int main() {
    std::vector<std::unique_ptr<SomeObject>> obj_vec;

    for (int i = 0; i < 3; ++i) {
        // 用make_unique在堆上创建对象,返回独占式智能指针
        auto ptr = std::make_unique<SomeObject>();
        std::cout << "当前指针地址:" << ptr.get() << std::endl; // 每次地址都不同

        // 转移所有权到vector
        obj_vec.push_back(std::move(ptr));
    }

    // 遍历vector验证地址
    std::cout << "\nvector中存储的指针地址:" << std::endl;
    for (const auto& p : obj_vec) {
        std::cout << p.get() << std::endl;
    }

    return 0;
}

二、为什么优先用std::make_unique而非new

1. 异常安全

如果用new直接创建对象并传递给unique_ptr,可能存在中间步骤的内存泄漏风险。比如:

// 危险:如果another_func()抛出异常,new出来的内存会泄漏
func(std::unique_ptr<SomeObject>(new SomeObject()), another_func());

编译器可能先执行new SomeObject(),再执行another_func()——如果another_func()抛出异常,此时内存还没被unique_ptr接管,就会永久泄漏。

而std::make_unique是原子操作:它会先分配内存、构造对象,再直接返回unique_ptr,完全避免了这种中间状态的泄漏风险。

2. 代码简洁与避免重复

make_unique不需要重复书写类型名(配合auto使用时),代码更简洁:

// 简洁:只写一次SomeObject
auto ptr = std::make_unique<SomeObject>();

// 繁琐:重复书写SomeObject
std::unique_ptr<SomeObject> ptr(new SomeObject());

3. 避免裸指针暴露

直接用new会产生裸指针,增加误操作的风险(比如不小心把同一个裸指针交给多个智能指针,或者手动delete裸指针导致重复释放)。make_unique完全封装了内存分配的细节,从根源上减少这类错误。

4. 风格一致性

和std::make_shared保持一致的用法,统一用make_*系列函数创建智能指针,代码风格更统一,维护成本更低。

注:C11标准中没有std::make_unique,是C14才加入的。如果你的项目基于C++11,可以自己实现一个简易版本:

template<typename T, typename... Args>
std::unique_ptr<T> make_unique(Args&&... args) {
    return std::unique_ptr<T>(new T(std::forward<Args>(args)...));
}

总结实践要点

  • 永远不要将栈对象的地址交给智能指针,智能指针只管理堆内存
  • 转移unique_ptr所有权必须用std::move(),禁止直接拷贝
  • 优先使用std::make_unique创建unique_ptr,避免直接调用new
  • 尽量用智能指针替代裸指针,减少内存泄漏和野指针的风险

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.22 20:51:13