C++左值、右值、move移动函数

拷贝构造函数、赋值运算符

今天写线程池的时候发现,标准做法禁用了拷贝构造函数和赋值操作符

cpp 复制代码
ThreadPool(const ThreadPool&) = delete;            //禁用拷贝构造函数
ThreadPool& operator=(const ThreadPool&) = delete; //禁用赋值

当不写这俩禁用的时候,c++会默认生成

cpp 复制代码
ThreadPool(const ThreadPool& other)            // 逐成员拷贝
ThreadPool& operator=(const ThreadPool& other)  // 逐成员赋值

拷贝构造函数会将成员统统再复制一份------而线程池的成员 std::thread、std::mutex、std::condition_variable 本身都禁止拷贝,编译器会报错;

即使能拷贝,也会出现两个对象管理同一批线程、重复 join、重复 notify 的未定义行为。

显式 = delete 的效果

cpp 复制代码
ThreadPool a(4);
ThreadPool b(a);            // ❌ 编译错误:use of deleted function
a = b;                      // ❌ 编译错误:use of deleted function

编译期就强制拒绝任何复制语义,把错误从"运行期崩溃"提前到"编译不过"。

与移动语义的关系

删除的是拷贝,并未禁止移动;

如果以后想支持"转移所有权",可以再手动添加:

cpp 复制代码
ThreadPool(ThreadPool&&) noexcept = default;
ThreadPool& operator=(ThreadPool&&) noexcept = default;

目前单例或全局唯一池,直接把拷贝封死即可。

拷贝和移动的关系

  • "移动"在语言层面只是允许你把资源从源对象搬出来

  • 标准库的常见实现把它做成"指针置空",于是看起来源对象被"搬空";

二者通过重载函数签名区分,

cpp 复制代码
T(const T&  other);   // 拷贝构造
T& operator=(const T& other); // 拷贝赋值

T(T&&  other) noexcept;       // 移动构造
T& operator=(T&& other) noexcept; // 移动赋值
cpp 复制代码
T a;
T b = a;        // a 是左值 → 拷贝
T c = std::move(a); // 转成右值 → 移动
维度 拷贝 移动
源对象状态 保持不变 被"搬空"(合法但未指定),源值不保证是原样
资源开销 深复制,O(n) 内存/系统调用 仅交换指针,O(1)
异常安全 可能抛(内存不足) 通常 noexcept
默认生成规则 若用户未声明任何拷贝控制成员,编译器自动生成拷贝不会自动生成移动
相互影响 一旦用户显式声明拷贝成员 ,移动成员不会自动生成;反之亦然
经典用途 传值返回、容器扩容、需要保留原对象 传 unique_ptr、fstream、thread 等"不可拷贝"对象

注意这里被"搬空"的说法容易有歧义
标准库对移动构造函数的做法

左值右值

左值(lvalue)≈ 有名字、有持久地址;右值(rvalue)≈ 没名字、即将销毁的"临时"值。

cpp 复制代码
int  a = 1;        // a 是左值
int* p = &a;       // 能取地址 → 左值
std::string s = "hi";
s                  // s 也是左值
cpp 复制代码
int x = 3 + 4;     // 3+4 产生临时量 → 右值
std::string("tmp") // 临时 string → 右值
42                 // 字面量 → 右值
std::move(a)       // 把左值"转成"右值引用,本质还是右值 这里a是任意类型变量

详解拷贝和移动的区别

cpp 复制代码
T a;                // 1. 默认构造,a 里可能申请了资源(堆内存、文件句柄...)
T b = a;            // 2. 拷贝构造:T::T(const T&)
T c = std::move(a); // 3. 移动构造:T::T(T&&)

左值引用和c++11右值引用

T&T&& 都是"引用",但一个是传统左值引用,一个是C++11 引入的右值引用

它们最大的差别在于能绑定到什么东西、用来干什么。

能绑谁 ------ 重载决议的"筛选器"

cpp 复制代码
int  x = 42;          // 左值
int f();              // 返回 int 右值

void g(int&  lref);   // 1. 左值引用
void g(int&& rref);   // 2. 右值引用

g(x);   // 调用 1,因为 x 是左值
g(f()); // 调用 2,因为 f() 返回的是右值

T& 只能绑到左值(有名字、有地址的对象)。

T&& 只能绑到右值(临时对象、字面量、std::move 后的东西)。

类型系统里的"真面目"

引用折叠规则(模板/类型推导时)

cpp 复制代码
T&  &  → T&
T&  && → T&
T&& &  → T&
T&& && → T&&

只有 T&& && 才会保留成 T&&,其余全部塌成左值引用。

这让"万能引用"(转发引用)auto&& / template<class T> void foo(T&&) 可以同时接受左值和右值。

生命周期延长 ------ 都干一样的事

cpp 复制代码
const std::string& s1 = std::string("tmp"); // 左值引用 const 也能延长临时量
std::string&&      s2 = std::string("tmp"); // 右值引用同样延长

两句都把临时 string 的生命周期拉到与引用自身相同 ,差别只是s2 后来还能被移动走

代码 实际调用的重载 理由
std::string t1 = s1; string(const string&) 拷贝 s1 的类型是 const string&,只能匹配到 const 左值引用,没有移动接口
std::string t2 = s2; string(string&&) 移动 s2 的类型是 string&&可以绑定到移动构造函数

注意,一个变量可以同时是左值,然后类型又是右值引用

用途分工

引用类型 典型场景
T& 传参避免拷贝、运算符重载(operator=)、普通别名
const T& 只读大对象入参、绑定右值临时量
T&& 移动构造/移动赋值完美转发 (std::forward<T>)、资源窃取

unique_ptr的用法

这里 unique_ptr是个独占指针,

特性 说明
独占所有权 不允许拷贝,只能移动(move)
自动释放内存 unique_ptr 被销毁时,它所指向的对象也会被自动删除
轻量级 几乎没有额外开销,和裸指针大小相同
支持自定义删除器 可以指定删除方式(如 delete[]、文件关闭等)
cpp 复制代码
#include <memory>
#include <iostream>

int main() {
    std::unique_ptr<int> ptr = std::make_unique<int>(42);
    std::cout << *ptr << std::endl;  // 输出 42

    // 转移所有权
    std::unique_ptr<int> ptr2 = std::move(ptr);
    if (!ptr) {
        std::cout << "ptr 已为空" << std::endl;
    }

    return 0;  // ptr2 销毁时自动释放内存
}

当使用 move将 ptr的所有权移动给 ptr2后,ptr就变成了空指针。

相关推荐
拾荒的小海螺2 小时前
JAVA:Spring Boot3 新特性解析的技术指南
java·开发语言·spring boot
程序猿20232 小时前
Python每日一练---第二天:合并两个有序数组
开发语言·python
椰羊sqrt2 小时前
CVE-2025-4334 深度分析:WordPress wp-registration 插件权限提升漏洞
android·开发语言·okhttp·网络安全
Js_cold3 小时前
Verilog任务task
开发语言·fpga开发·verilog
njxiejing3 小时前
Numpy一维、二维、三维数组切片实例
开发语言·python·numpy
许长安3 小时前
c/c++ static关键字详解
c语言·c++·经验分享·笔记
一位搞嵌入式的 genius3 小时前
前端实战开发(四):从迭代器到异步编程:ES6 Generator 全面解析 + 实战问题排查
开发语言·前端·es6·前端实战
来来走走3 小时前
Android开发(Kotlin) 高阶函数、内联函数
android·开发语言·kotlin
Murphy_lx3 小时前
C++ thread类
开发语言·c++