拷贝构造 vs 移动构造:从左值引用到右值引用彻底讲透(Student 示例)

很多人学 C++ 时都会卡在这几个点:

  • 左值引用和右值引用到底区别是什么?
  • 为什么拷贝构造用 const T&
  • 为什么移动构造用 T&&
  • 右值引用是不是马上销毁?
  • 为什么移动后要置 nullptr?

这篇文章用一个最简单的 Student 类,把这些问题一次讲透。

一、一个有资源的类

我们写一个最小可运行示例:

cpp 复制代码
#include <iostream>
using namespace std;

class Student {
public:
    int* age;

    Student(int a) : age(new int(a)) {
        cout << "Ctor      this=" << this
             << " age_ptr=" << age
             << " val=" << *age << endl;
    }

    // 拷贝构造(深拷贝)
    Student(const Student& other)
        : age(new int(*other.age)) {
        cout << "CopyCtor  this=" << this
             << " age_ptr=" << age
             << " val=" << *age << endl;
    }

    // 移动构造
    Student(Student&& other) noexcept
        : age(other.age) {
        other.age = nullptr;
        cout << "MoveCtor  this=" << this
             << " age_ptr=" << age << endl;
    }

    ~Student() {
        cout << "Dtor      this=" << this
             << " age_ptr=" << age << endl;
        delete age;
    }
};

这里有一个关键点:

age 是堆内存资源。

只要类里有资源,拷贝和移动问题就一定会出现。

二、为什么拷贝构造用 const T&?

cpp 复制代码
Student(const Student& other)

这是 左值引用

它能绑定:

cpp 复制代码
Student a(20);
Student b = a;  // 走拷贝构造

因为:

  • a 是左值(有名字,可寻址)
  • 左值只能绑定 T&

为什么加 const

因为:

左值通常还会继续被使用

你不能掏空它的资源。

所以必须:

cpp 复制代码
const Student&

防止误修改。

三、深拷贝在做什么?

关键语句:

cpp 复制代码
age = new int(*other.age);

拆解:

  1. other.ageint*
  2. *other.age 取出值(比如 20)
  3. new int(20) 开新内存
  4. 把新地址赋给 age

内存图:

cpp 复制代码
other.age ----> 0x1000 (20)
this->age ----> 0x2000 (20)

两份资源。

这就是深拷贝。

四、为什么移动构造用 T&&?

cpp 复制代码
Student(Student&& other)

&& 是右值引用。

它能绑定:

cpp 复制代码
Student b = Student(30);   // 临时对象
Student c = std::move(a);  // 被标记为右值

右值通常是:

  • 临时对象
  • 即将被销毁的对象
  • 或被允许"掏空"的对象

所以可以:

cpp 复制代码
age = other.age;
other.age = nullptr;

五、移动构造在做什么?

cpp 复制代码
Student(Student&& other) noexcept
    : age(other.age) {
    other.age = nullptr;
}

执行流程:

移动前:

cpp 复制代码
other.age ----> 0x1000 (20)

移动后:

cpp 复制代码
this->age  ----> 0x1000 (20)
other.age  ----> nullptr

没有分配新内存。

没有复制数据。

只是:

转移所有权

六、左值引用 vs 右值引用区别

对比项 左值引用 T& 右值引用 T&&
绑定对象 左值 右值
是否允许修改 看是否 const 允许修改
是否允许转移资源 不允许 允许
典型用途 拷贝 移动

七、重要知识点:右值引用参数在函数内部是左值

这是很多人最容易混乱的地方。

cpp 复制代码
Student(Student&& other)

虽然 other 类型是右值引用,

但在函数内部:

cpp 复制代码
other

左值

因为:

只要一个变量有名字,它就是左值。

这就是为什么:

如果你在函数内部再传递 other

必须写:

cpp 复制代码
std::move(other)

否则会走拷贝构造。

八、右值引用是不是马上销毁?

不是。

销毁由作用域决定。

例如:

cpp 复制代码
Student&& r = Student(40);

临时对象生命周期会延长到 r 结束。

所以:

右值引用 ≠ 马上销毁

右值引用 = 允许资源转移

九、拷贝 vs 移动本质区别

项目 拷贝 移动
是否分配内存
是否复制数据
是否转移所有权
性能

十、终极理解

cpp 复制代码
Student b = a;              // 拷贝
Student c = std::move(a);   // 移动
Student d = Student(50);    // 移动

拷贝:

保留原对象完整性

移动:

掏空原对象,把资源转走

总结

  1. 左值引用用于拷贝
  2. 右值引用用于移动
  3. 移动语义是资源所有权转移
  4. 引用不决定生命周期
  5. 右值引用参数在函数内部是左值
相关推荐
myloveasuka7 小时前
Java与C++多态访问成员变量/方法 对比
java·开发语言·c++
2301_821700537 小时前
C++编译期多态实现
开发语言·c++·算法
奥地利落榜美术生灬7 小时前
c++ 锁相关(mutex 等)
开发语言·c++
xixihaha13247 小时前
C++与FPGA协同设计
开发语言·c++·算法
小小怪7508 小时前
C++中的函数式编程
开发语言·c++·算法
Yupureki8 小时前
《MySQL数据库基础》1. 数据库基础
c语言·开发语言·数据库·c++·mysql·oracle·github
汉克老师9 小时前
GESP2026年3月认证C++二级( 第二部分判断题(1-10))
c++·gesp三级·gesp3级
样例过了就是过了10 小时前
LeetCode热题100 N 皇后
数据结构·c++·算法·leetcode·dfs·深度优先遍历
C++ 老炮儿的技术栈12 小时前
Linux 文件系统目录架构全解析
linux·服务器·c语言·开发语言·c++
样例过了就是过了12 小时前
LeetCode热题100 分割回文串
数据结构·c++·算法·leetcode·深度优先·dfs