跳转到内容

C++ make unique

来自代码酷

C++ make_unique[编辑 | 编辑源代码]

介绍[编辑 | 编辑源代码]

make_unique 是 C++14 引入的一个实用函数模板,用于创建并返回一个指向新对象的 std::unique_ptr。它是现代 C++ 中智能指针工具集的重要组成部分,旨在提供一种更安全、更高效的方式来管理动态分配的内存,避免内存泄漏和裸指针的使用。

std::make_unique 的主要优势包括:

  • 减少显式的 newdelete 操作,降低内存泄漏的风险。
  • 提供异常安全性,确保在构造函数抛出异常时不会泄漏内存。
  • 代码更简洁,避免重复书写类型名称。

语法[编辑 | 编辑源代码]

make_unique 的基本语法如下:

template< class T, class... Args >
std::unique_ptr<T> make_unique( Args&&... args );

其中:

  • T 是要创建对象的类型。
  • Args 是传递给 T 的构造函数的参数列表。

基本用法[编辑 | 编辑源代码]

创建单个对象[编辑 | 编辑源代码]

最简单的用法是创建一个动态分配的单一对象:

#include <memory>
#include <iostream>

struct Point {
    int x, y;
    Point(int x, int y) : x(x), y(y) {}
};

int main() {
    auto ptr = std::make_unique<Point>(3, 4);
    std::cout << "Point: (" << ptr->x << ", " << ptr->y << ")\n";
    return 0;
}

输出:

Point: (3, 4)

创建数组[编辑 | 编辑源代码]

make_unique 也可以用于创建动态数组:

#include <memory>
#include <iostream>

int main() {
    auto arr = std::make_unique<int[]>(5); // 创建包含5个int的数组
    for (int i = 0; i < 5; ++i) {
        arr[i] = i * 10;
    }
    
    for (int i = 0; i < 5; ++i) {
        std::cout << arr[i] << " ";
    }
    return 0;
}

输出:

0 10 20 30 40

与直接使用 new 的对比[编辑 | 编辑源代码]

传统方式使用 new 创建 unique_ptr

std::unique_ptr<Point> ptr(new Point(3, 4));

使用 make_unique 的改进版本:

auto ptr = std::make_unique<Point>(3, 4);

优势对比: 1. 更简洁,不需要重复类型名称 2. 更安全,避免了潜在的异常安全问题 3. 更高效,可能减少内存分配次数

异常安全性[编辑 | 编辑源代码]

make_unique 提供了更强的异常安全性。考虑以下可能不安全的代码:

void unsafe_function(int a, int b) {
    some_function(std::unique_ptr<Point>(new Point(a, b)), std::unique_ptr<Point>(new Point(b, a)));
}

如果其中一个 new 成功而另一个抛出异常,则可能发生内存泄漏。使用 make_unique 可以避免这个问题:

void safe_function(int a, int b) {
    some_function(std::make_unique<Point>(a, b), std::make_unique<Point>(b, a));
}

实际应用案例[编辑 | 编辑源代码]

工厂模式[编辑 | 编辑源代码]

make_unique 非常适合用于工厂方法:

#include <memory>
#include <iostream>

class Shape {
public:
    virtual void draw() = 0;
    virtual ~Shape() = default;
};

class Circle : public Shape {
public:
    void draw() override { std::cout << "Drawing a circle\n"; }
};

class Square : public Shape {
public:
    void draw() override { std::cout << "Drawing a square\n"; }
};

enum class ShapeType { Circle, Square };

std::unique_ptr<Shape> create_shape(ShapeType type) {
    switch (type) {
        case ShapeType::Circle: return std::make_unique<Circle>();
        case ShapeType::Square: return std::make_unique<Square>();
    }
    return nullptr;
}

int main() {
    auto circle = create_shape(ShapeType::Circle);
    auto square = create_shape(ShapeType::Square);
    
    circle->draw();
    square->draw();
    return 0;
}

输出:

Drawing a circle
Drawing a square

资源管理[编辑 | 编辑源代码]

make_unique 可以用于管理各种资源:

#include <memory>
#include <fstream>

class FileHandler {
    std::unique_ptr<std::fstream> file;
public:
    FileHandler(const std::string& filename) 
        : file(std::make_unique<std::fstream>(filename)) {
        if (!file->is_open()) {
            throw std::runtime_error("Failed to open file");
        }
    }
    // ... 其他文件操作方法
};

性能考虑[编辑 | 编辑源代码]

make_unique 通常比直接使用 new 更高效,因为: 1. 它允许编译器进行更好的优化 2. 它可能减少内存分配的次数(通过合并分配) 3. 它避免了某些类型的重复计算

限制[编辑 | 编辑源代码]

1. 不能用于需要自定义删除器的场景 2. 不能直接用于需要共享所有权的情况(此时应使用 make_shared) 3. C++14 之前的标准不支持(但可以自己实现)

自定义实现(C++11兼容)[编辑 | 编辑源代码]

如果你需要使用 C++11,可以自己实现 make_unique

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

总结[编辑 | 编辑源代码]

std::make_unique 是现代 C++ 中创建 unique_ptr 的推荐方式,它提供了:

  • 更简洁的语法
  • 更好的异常安全性
  • 更高的代码可读性
  • 潜在的性能优势

在大多数情况下,应该优先使用 make_unique 而不是直接使用 new 来创建 unique_ptr

参见[编辑 | 编辑源代码]