c++ make_unique用法

std::make_unique 使用详解

std::make_unique 是 C++14 引入的智能指针工厂函数,用于安全高效地创建 std::unique_ptr 对象。以下是完整使用指南:


1. 基本用法
#include 

// 创建内置类型
auto intPtr = std::make_unique<int>(42);  // 指向 int(42)

// 创建自定义类
class MyClass {
public:
    MyClass(int a, double b) : m_a(a), m_b(b) {}
private:
    int m_a;
    double m_b;
};

auto objPtr = std::make_unique<MyClass>(10, 3.14);  // 调用构造函数

2. 创建数组(C++14 起支持)
// 创建原始数组
auto arrPtr = std::make_unique<int[]>(5);  // 5个int的数组

// 初始化数组元素
for (int i = 0; i < 5; ++i) {
    arrPtr[i] = i * 10;
}

// 创建类对象数组
class Item {
public:
    Item() { std::cout << "Item created\n"; }
    ~Item() { std::cout << "Item destroyed\n"; }
};

auto items = std::make_unique<Item[]>(3);  // 创建3个Item对象
// 输出:
// Item created
// Item created
// Item created
// (离开作用域后)
// Item destroyed
// Item destroyed
// Item destroyed

3. 与直接构造的对比
// 方式1: 使用make_unique(推荐)
auto ptr1 = std::make_unique<MyClass>(1, 2.0);

// 方式2: 直接构造(有潜在风险)
std::unique_ptr<MyClass> ptr2(new MyClass(1, 2.0));

// 为什么推荐make_unique:
// 1. 异常安全:防止内存泄漏
// 2. 代码简洁:避免重复类型声明
// 3. 性能优化:单次内存分配(与make_shared类似)

4. 自定义删除器(需直接构造)
// 自定义删除器
auto fileDeleter = [](FILE* f) {
    std::cout << "Closing file\n";
    if (f) fclose(f);
};

// make_unique不支持自定义删除器
// auto file = std::make_unique(fopen("test.txt", "r")); // 错误

// 正确方式:直接构造unique_ptr
std::unique_ptr<FILE, decltype(fileDeleter)> 
    filePtr(fopen("test.txt", "r"), fileDeleter);

5. 在容器中的使用
#include 

// 存储unique_ptr的容器
std::vector<std::unique_ptr<MyClass>> objects;

// 添加元素(需要移动语义)
objects.push_back(std::make_unique<MyClass>(1, 1.0));
objects.emplace_back(std::make_unique<MyClass>(2, 2.0));

// 遍历访问
for (const auto& ptr : objects) {
    std::cout << ptr->value() << "\n";
}

6. 作为函数返回值
// 工厂函数示例
std::unique_ptr<MyClass> createObject(int type) {
    switch (type) {
        case 1: return std::make_unique<MyClass>(10, 5.5);
        case 2: return std::make_unique<MyClass>(20, 6.6);
        default: return nullptr;
    }
}

// 调用
auto obj = createObject(1);

7. C++11 兼容方案(若未支持 make_unique)
#if __cplusplus < 201402L  // 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)...));
}

// 数组特化版本
template<typename T>
std::unique_ptr<T> make_unique(size_t size) {
    return std::unique_ptr<T>(new typename std::remove_extent<T>::type[size]());
}
#endif

8. 关键优势总结
  1. 异常安全:防止构造函数抛出异常导致内存泄漏

    process(std::unique_ptr<A>(new A), std::unique_ptr<B>(new B));
    // 若 new A 成功,new B 抛出异常,则 A 会泄漏
    
    process(std::make_unique<A>(), std::make_unique<B>()); // 安全
    
  2. 代码简洁性:避免重复类型名称

    // 冗余写法
    std::unique_ptr<MyVeryLongTypeName> ptr(new MyVeryLongTypeName);
    
    // 简洁写法
    auto ptr = std::make_unique<MyVeryLongTypeName>();
    
  3. 性能优化

    • 减少内存分配次数(对象+智能指针控制块)
    • 更好的缓存局部性(对象和控制块相邻)

9. 使用限制
  1. 不支持自定义删除器(需直接构造 unique_ptr

  2. 不能用于初始化列表构造函数

    struct InitList {
        InitList(std::initializer_list<int>) {}
    };
    
    // auto p = std::make_unique({1,2,3}); // 错误
    auto p = std::unique_ptr<InitList>(new InitList{1,2,3}); // 正确
    
  3. 无法访问私有构造函数(需使用友元工厂函数)

最佳实践:除需要自定义删除器或特殊构造的场景外,优先使用 std::make_unique 创建独占指针对象。

你可能感兴趣的:(c/c++,c++,开发语言)