std::unique_ptr成员函数用法

首先需要注意,std::unique_ptr 本身显式禁用了拷贝构造函数和拷贝赋值运算符。

std::unique_ptr 实现了独占所有权语义(Exclusive Ownership),确保同一时间内只有一个 unique_ptr 可以拥有并管理指定的对象资源。

源码实现逻辑

在 C++ 标准库中,std::unique_ptr 通过 = delete 显式禁用了拷贝操作:

cpp 复制代码
template <typename T, typename Deleter = std::default_delete<T>>
class unique_ptr {
public:
    // 禁用了拷贝构造函数
    unique_ptr(const unique_ptr&) = delete;

    // 禁用了拷贝赋值运算符
    unique_ptr& operator=(const unique_ptr&) = delete;

    // 允许移动构造函数和移动赋值运算符
    unique_ptr(unique_ptr&&) noexcept;
    unique_ptr& operator=(unique_ptr&&) noexcept;
};

行为表现与代码示例

1. 尝试拷贝(编译错误)

试图直接复制 std::unique_ptr 会在编译期直接报错:

cpp 复制代码
#include <memory>

int main() {
    auto p1 = std::make_unique<int>(42);
    
    // auto p2 = p1;       // 编译错误!拷贝构造函数已被禁用 (= delete)
    // auto p2;
    // p2 = p1;            // 编译错误!拷贝赋值运算符已被禁用 (= delete)
}

2. 所有权转移(移动语义)

虽然不能拷贝,但可以通过 std::move 将对象的所有权转让给另一个 unique_ptr。转移后,原指针会变为空指针(nullptr):

cpp 复制代码
#include <memory>
#include <iostream>

int main() {
    auto p1 = std::make_unique<int>(42);
    
    // 移交所有权
    std::unique_ptr<int> p2 = std::move(p1); 

    // 验证状态
    if (p1 == nullptr) {
        std::cout << "p1 已经变为空指针\n";
    }
    std::cout << "*p2: " << *p2 << "\n"; // 输出 42
}

3. 对自定义类(Class/Struct)的影响

如果一个自定义类将 std::unique_ptr 作为成员变量,由于 unique_ptr 无法拷贝,编译器会自动禁用该自定义类的默认拷贝构造函数和拷贝赋值运算符。

cpp 复制代码
struct Widget {
    std::unique_ptr<int> data;
};

int main() {
    Widget w1;
    // Widget w2 = w1; // 编译错误!Widget 的默认拷贝操作已被自动隐式禁用
    Widget w2 = std::move(w1); // 正确!Widget 仍然支持移动
}

只要你的类中包含了 std::unique_ptr 类型的成员,编译器在发现你尝试拷贝该类的对象时,就会直接在编译期报错。

如果你的业务场景确实需要这个类支持"拷贝",可以通过深拷贝(Deep Copy)手动实现拷贝构造函数。

默认行为:编译器自动禁用拷贝

根据 C++ 标准规定,如果一个类的某个成员变量不可拷贝(例如 std::unique_ptr 的拷贝构造函数是 = delete),编译器将隐式禁用该类的默认拷贝构造函数和默认拷贝赋值运算符。

cpp 复制代码
#include <memory>

class MyClass {
    std::unique_ptr<int> data_;
public:
    MyClass(int val) : data_(std::make_unique<int>(val)) {}
    
    // 注意:这里我们没有写任何 = delete 或拷贝构造函数
};

int main() {
    MyClass obj1(42);

    // 编译报错!编译器无法为你生成默认的拷贝构造函数
    // MyClass obj2 = obj1; 

    // 正确!移动语义依然有效(编译器会自动生成默认的移动构造函数)
    MyClass obj3 = std::move(obj1); 
}

如果你"不需要"这个类被拷贝(最常见)

你不需要 在类里手动写 MyClass(const MyClass&) = delete;。

不过,根据 C++ 5法则(Rule of 5),如果你想让代码意图更清晰,可以显式声明(但这仅仅是为了提供更友好的代码可读性,对编译结果没有本质影响):

cpp 复制代码
class MyClass {
    std::unique_ptr<int> data_;
public:
    MyClass(int val) : data_(std::make_unique<int>(val)) {}

    // 【可选】显式写出以提高代码可读性
    MyClass(const MyClass&) = delete;
    MyClass& operator=(const MyClass&) = delete;

    // 如果显式写了 delete,建议显式声明移动操作
    MyClass(MyClass&&) noexcept = default;
    MyClass& operator=(MyClass&&) noexcept = default;
};

如果你"需要"这个类支持拷贝

如果你希望你的类可以被拷贝(例如拷贝时复制一份全新的底层资源),你可以手动编写拷贝构造函数实现深拷贝:

cpp 复制代码
#include <memory>

class MyClass {
    std::unique_ptr<int> data_;
public:
    MyClass(int val) : data_(std::make_unique<int>(val)) {}

    // 手动实现【深拷贝构造函数】
    MyClass(const MyClass& other) {
        if (other.data_) {
            // 为当前对象重新分配内存,并复制值
            data_ = std::make_unique<int>(*other.data_); 
        }
    }

    // 手动实现【深拷贝赋值运算符】
    MyClass& operator=(const MyClass& other) {
        if (this != &other) {
            if (other.data_) {
                data_ = std::make_unique<int>(*other.data_);
            } else {
                data_.reset();
            }
        }
        return *this;
    }

    // 显式保留默认移动操作
    MyClass(MyClass&&) noexcept = default;
    MyClass& operator=(MyClass&&) noexcept = default;
};

int main() {
    MyClass obj1(42);
    MyClass obj2 = obj1; // 正常工作!obj2 拥有自己的 unique_ptr 和独立的内存区域
}

验证步骤

要验证某个类型是否可拷贝,可以使用静态断言:

cpp 复制代码
#include <memory>
#include <type_traits>

// 验证不可拷贝
static_assert(!std::is_copy_constructible_v<std::unique_ptr<int>>, "unique_ptr 应禁用拷贝构造");
static_assert(!std::is_copy_assignable_v<std::unique_ptr<int>>, "unique_ptr 应禁用拷贝赋值");

// 验证支持移动
static_assert(std::is_move_constructible_v<std::unique_ptr<int>>, "unique_ptr 应支持移动构造");
static_assert(std::is_move_assignable_v<std::unique_ptr<int>>, "unique_ptr 应支持移动赋值");

end

相关推荐
C^h5 小时前
c++ string
开发语言·c++
raindayinrain5 小时前
c++高性能--原子操作,无锁编程,操作顺序
开发语言·c++·性能优化
学逆向的5 小时前
C++ 合并命名空间
开发语言·c++·网络安全
Zhao-Jichao5 小时前
VS2026 打包 C++ 程序生成 EXE,借助 Installer Project 扩展工具
开发语言·c++
小徐不徐说7 小时前
C++ 接口与插件设计:从工厂注册到 Qt 动态插件
数据库·c++·qt·系统架构·程序设计
大都督会赢的7 小时前
C++第一节课
开发语言·c++
UIU1147 小时前
P1233 [ICPC 2001 Taejon R] 木棍加工
数据结构·c++·算法·动态规划
TMedical7 小时前
MedPlanning 截骨模拟矫形功能
c++·图像处理·人工智能·qt·健康医疗
qq_406981038 小时前
共享变量一 关于extern与const
开发语言·c++
chari克里10 小时前
QT5.15.2 + msvc2022部署全流程说明(windows可联网环境)
开发语言·c++·qt