Limesn
Limesn
发布于 2026-10-06 / 0 阅读
0

第十章 移动语义与智能指针

1. 什么是移动语义?

移动语义(Move Semantics) 是C++11引入的一项重要特性。它的核心思想是资源的转移(窃取),而不是复制。

  • 背景:在旧标准中,当我们将一个临时对象(右值)赋值或拷贝给另一个对象时,往往会发生深拷贝,这需要分配新内存并复制数据,效率很低。
  • 解决:移动语义允许我们将临时对象所拥有的资源(如堆内存、文件句柄)直接“转移”给新对象,然后将临时对象置于一个合法但未定义的状态。这样避免了不必要的深拷贝,极大地提高了程序性能。

2. 什么是左值引用,什么是右值引用,有什么特点?

  • 左值 (L-value):有明确名字、可以取地址的表达式(例如变量 int a = 5; 中的 a)。
  • 右值 (R-value):没有名字、不能取地址的临时值(例如字面量 5,或表达式 a + b 的结果)。
  • 左值引用 (T&):绑定到左值。传统C++的引用,不能绑定到临时对象。
  • 右值引用 (T&&):C++11引入,只能绑定到右值(临时对象)。它是实现移动语义和完美转发的基础。
  • 特点:
    • 右值引用延长了临时对象的生命周期。
    • 右值引用本身是一个左值(因为它有名字,有地址),但它的类型是右值引用。
    • 利用右值引用,编译器可以区分出“这个对象即将销毁,可以直接窃取它的资源”。

3. 移动构造,移动赋值运算符函数的语法形式是什么?

  • 移动构造函数:
    ClassName(ClassName&& other) noexcept 
        : // 成员初始化列表,直接接管 other 的资源
    {
        // 将 other 的内部指针置为 nullptr 等安全状态
    }
    
  • 移动赋值运算符:
    ClassName& operator=(ClassName&& other) noexcept 
    {
        if (this != &other) { // 避免自赋值
            // 释放当前对象的资源
            // 接管 other 的资源
            // 将 other 置为安全状态
        }
        return *this;
    }
    
  • 注意:通常需要加上 noexcept 关键字,否则标准库容器(如 std::vector)在扩容时可能出于异常安全考虑,不会调用移动构造,而是退化为拷贝构造。

4. std::move()有什么作用?

std::move() 在运行期实际上什么都不做。它只是一个类型转换函数(本质上是一个静态转换 static_cast)。

  • 它的作用是将一个左值强制转换为右值引用(T&&)。
  • 它的真正意义在于告诉编译器:“这个对象我不再需要了,你可以把它当作右值来处理,允许调用移动构造函数或移动赋值运算符来窃取它的资源。”
  • 警告:使用 std::move 后,不要再去使用被移动的对象(除非你重新赋值给它),因为它的状态通常是未定义的。

5. RAII技术是什么?特点是什么?

  • RAII (Resource Acquisition Is Initialization),即“资源获取即初始化”。
  • 核心思想:将资源的生命周期与对象的生命周期绑定。在对象的构造函数中获取资源,在析构函数中释放资源。
  • 特点:
    • 自动管理:依赖C++的自动析构机制,无需手动调用 delete 或 free。
    • 异常安全:即使程序在中间抛出异常,栈展开(Stack Unwinding)时也会自动调用对象的析构函数,从而释放资源,避免了内存泄漏。
    • 典型应用:智能指针(std::unique_ptr, std::shared_ptr)、文件流(std::fstream)、互斥锁(std::lock_guard)。

6. 智能指针有哪些?特点分别是什么?

C++11 标准库(<memory>)提供了三种主要的智能指针,它们都是 RAII 的典型实现:

  1. std::unique_ptr (独占式智能指针)

    • 特点:独占资源的所有权,同一时间只能有一个 unique_ptr 指向该对象。
    • 行为:禁止拷贝构造和拷贝赋值,但支持移动构造和移动赋值(所有权转移)。
    • 开销:几乎零开销,和裸指针一样大。
    • 场景:默认首选,用于独占资源的场景。
  2. std::shared_ptr (共享式智能指针)

    • 特点:多个 shared_ptr 可以共享同一个对象的所有权。
    • 行为:内部使用**引用计数(Reference Counting)**机制。每增加一个指向该对象的 shared_ptr,引用计数+1;每销毁一个,引用计数-1。当计数为0时,自动释放对象。
    • 开销:比裸指针大(需要维护控制块,包含引用计数等),且存在线程安全问题(引用计数的增减是原子操作,但对象本身不是)。
    • 场景:需要多个对象共享资源所有权时。
  3. std::weak_ptr (弱引用智能指针)

    • 特点:它不控制对象的生命周期,不增加引用计数。它指向由 std::shared_ptr 管理的对象。
    • 作用:主要用来解决 shared_ptr 的循环引用(Circular Reference)问题。循环引用会导致引用计数永远不为0,从而内存泄漏。
    • 行为:不能直接解引用,需要通过 lock() 方法提升为 shared_ptr(如果对象已被释放,则返回空的 shared_ptr)来访问对象。
    • 场景:观察者模式、打破循环引用。

(注:还有C++98的 std::auto_ptr,但因其设计缺陷已在C++11被弃用,C++17被移除。此外,C++11还引入了 std::make_shared 和 std::make_unique (C++14),推荐优先使用它们来创建智能指针,以获得更好的异常安全和性能。)