MST

星途 面试题库

面试题:C++ std::unique_ptr异常安全的实现细节

详细阐述std::unique_ptr是如何确保异常安全的,从它的构造函数、析构函数以及赋值操作等方面进行分析。假设存在一个类A,其中有一个std::unique_ptr成员变量,在A的拷贝构造函数和移动构造函数中,如何保证对std::unique_ptr的处理符合异常安全原则?
30.7万 热度难度
编程语言C++

知识考点

AI 面试

面试题答案

一键面试

std::unique_ptr确保异常安全分析

  1. 构造函数
    • std::unique_ptr的构造函数通常是无异常抛出的。例如,std::unique_ptr<int> ptr(new int(5));new int(5)成功分配内存后,std::unique_ptr的构造函数会接管这个指针的所有权。如果new int(5)抛出异常(比如内存分配失败),std::unique_ptr不会持有一个无效指针,因为它的构造函数还未执行完成,也就不存在资源泄漏问题,符合异常安全的基本要求。
  2. 析构函数
    • std::unique_ptr的析构函数会自动释放它所管理的资源。当std::unique_ptr对象生命周期结束时(无论是正常结束还是因为异常导致栈展开),析构函数会被调用。例如:
    {
        std::unique_ptr<int> ptr(new int(5));
    } // 这里`ptr`的析构函数会被调用,释放`new int(5)`分配的内存
    
    • 由于析构函数是自动调用且释放资源的操作是确定的,不会抛出异常,所以在异常发生导致栈展开时,资源能够被正确释放,满足异常安全中的“无泄漏”原则。
  3. 赋值操作
    • 对于std::unique_ptr的赋值操作(operator =),它会先释放当前所管理的资源(如果有),然后接管新的指针。例如:
    std::unique_ptr<int> ptr1(new int(5));
    std::unique_ptr<int> ptr2(new int(10));
    ptr1 = std::move(ptr2);
    
    • ptr1 = std::move(ptr2)中,ptr1会先释放它原来管理的int(5)(如果有),然后接管ptr2的指针。这个过程中,如果接管新指针时抛出异常(比如在某些自定义的资源接管逻辑中),ptr1会恢复到赋值前的状态(持有原来的资源或为nullptr),而ptr2会释放资源(因为已经std::move了),符合异常安全中的“基本保证”原则(操作失败时,对象处于有效但未指定的状态)。

类A中std::unique_ptr处理符合异常安全原则

  1. 拷贝构造函数
    • std::unique_ptr是不可拷贝的,因为它的设计理念就是独占资源。如果类A中有一个std::unique_ptr成员变量,在类A的拷贝构造函数中不能简单地拷贝std::unique_ptr。例如:
    class A {
    private:
        std::unique_ptr<int> data;
    public:
        A(const A& other) = delete; // 显式删除拷贝构造函数,避免意外拷贝导致资源管理问题
    };
    
    • 如果确实需要拷贝资源,应该重新分配内存并拷贝数据。例如:
    class A {
    private:
        std::unique_ptr<int> data;
    public:
        A(const A& other) {
            if (other.data) {
                data.reset(new int(*other.data));
            }
        }
    };
    
    • 在这个过程中,如果new int(*other.data)抛出异常,data不会被赋值,对象A不会处于无效状态,符合异常安全的“基本保证”原则。
  2. 移动构造函数
    • 在类A的移动构造函数中,std::unique_ptr的处理应该是安全的。例如:
    class A {
    private:
        std::unique_ptr<int> data;
    public:
        A(A&& other) noexcept : data(std::move(other.data)) {}
    };
    
    • 这里使用std::move来转移std::unique_ptr的所有权。由于std::unique_ptr的移动构造函数是noexcept的,不会抛出异常,所以在移动过程中如果发生异常(在移动构造函数外部),other的资源已经被转移,data持有正确的资源,符合异常安全原则。同时,移动构造函数标记为noexcept也向调用者表明此操作不会抛出异常,让调用者可以进行更优化的处理。