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)。
- 自动管理:依赖C++的自动析构机制,无需手动调用
6. 智能指针有哪些?特点分别是什么?
C++11 标准库(<memory>)提供了三种主要的智能指针,它们都是 RAII 的典型实现:
-
std::unique_ptr(独占式智能指针)- 特点:独占资源的所有权,同一时间只能有一个
unique_ptr指向该对象。 - 行为:禁止拷贝构造和拷贝赋值,但支持移动构造和移动赋值(所有权转移)。
- 开销:几乎零开销,和裸指针一样大。
- 场景:默认首选,用于独占资源的场景。
- 特点:独占资源的所有权,同一时间只能有一个
-
std::shared_ptr(共享式智能指针)- 特点:多个
shared_ptr可以共享同一个对象的所有权。 - 行为:内部使用**引用计数(Reference Counting)**机制。每增加一个指向该对象的
shared_ptr,引用计数+1;每销毁一个,引用计数-1。当计数为0时,自动释放对象。 - 开销:比裸指针大(需要维护控制块,包含引用计数等),且存在线程安全问题(引用计数的增减是原子操作,但对象本身不是)。
- 场景:需要多个对象共享资源所有权时。
- 特点:多个
-
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),推荐优先使用它们来创建智能指针,以获得更好的异常安全和性能。)