1. 移动语义和拷贝语义的区别
拷贝语义
- 概念:拷贝语义是指在创建一个新对象时,将已有对象的数据进行复制,使得新对象拥有与原对象相同的数据副本。通过拷贝构造函数和拷贝赋值运算符来实现。
- 拷贝构造函数:当用一个已存在的对象初始化一个新对象时被调用,例如
MyClass obj2(obj1);
这里就会调用MyClass
的拷贝构造函数来创建obj2
,并将obj1
的数据成员逐个复制到obj2
中。 - 拷贝赋值运算符:当把一个已存在的对象赋值给另一个已存在的对象时调用,比如
obj2 = obj1;
就会调用拷贝赋值运算符,将obj1
的数据成员赋值到obj2
的对应数据成员上。 - 性能影响:对于简单的数据类型(如基本数据类型、小型结构体等),拷贝操作通常比较高效。但对于包含动态分配内存、文件句柄等资源的复杂对象,拷贝语义可能会导致大量的内存分配和数据复制,这在性能和资源利用上可能是低效的,甚至可能出现浅拷贝导致的资源管理问题(如多个对象共享同一块动态分配的内存,在析构时会多次释放引发错误)。
移动语义
- 概念:移动语义是 C++ 11 引入的特性,它允许将资源(如动态分配的内存、文件句柄等)从一个对象转移到另一个对象,而不是进行昂贵的复制操作。移动操作完成后,源对象通常处于一种可析构但可能不再能正常使用的状态(比如原来指向动态内存的指针被置为
nullptr
)。 - 移动构造函数:当用一个临时对象(即将要销毁的对象,如函数返回值)初始化一个新对象时,会优先调用移动构造函数来实现资源的移动。例如,函数返回一个包含大量动态分配数据的对象,使用移动构造函数可以高效地将该对象的资源所有权转移到调用函数处的接收对象,而无需复制所有数据。形式上类似
MyClass obj2(std::move(obj1));
,这里的std::move
是一个将对象标记为可移动的转换函数,它实际上只是将对象强制转换为右值引用,告诉编译器可以对其进行移动操作,调用移动构造函数。 - 移动赋值运算符:类似于移动构造函数,当把一个即将销毁的对象(右值)赋值给一个已存在的对象时,会调用移动赋值运算符来实现资源的转移。如
obj2 = std::move(obj1);
。 - 性能优势:对于包含大量资源的对象,移动语义可以显著提高程序的性能,避免不必要的资源复制,特别是在处理临时对象、函数返回值等场景中非常有用。
2. C++ 中的智能指针及类型
智能指针的概念
在 C++ 中,普通指针在管理动态分配的内存等资源时存在风险,比如忘记释放内存导致内存泄漏,或者对同一块内存进行多次释放导致程序崩溃等问题。智能指针是一种类模板,它的行为类似常规指针,但能够自动管理所指向对象的生命周期,通过在合适的时候自动释放所指向的资源来避免这些常见的指针管理错误。
智能指针的类型
std::unique_ptr
:- 独占所有权:
std::unique_ptr
对其指向的对象拥有独占所有权,这意味着同一时刻只有一个std::unique_ptr
可以指向该对象。当std::unique_ptr
被销毁(例如离开作用域)时,它所指向的对象也会自动被销毁(通过调用对象的析构函数)。 - 示例用法:
-
#include <memory>class MyClass { public:~MyClass() {// 析构函数逻辑} };int main() {std::unique_ptr<MyClass> ptr(new MyClass);// 在这里可以正常使用 ptr 指向的 MyClass 对象// 当离开 main 函数作用域时,ptr 所指向的 MyClass 对象会自动被析构return 0; }
std::shared_ptr
:- 共享所有权:
std::shared_ptr
允许多个std::shared_ptr
实例指向同一个对象,并且会自动维护一个引用计数。每当一个新的std::shared_ptr
指向该对象时,引用计数增加;当一个std::shared_ptr
不再指向该对象(如被销毁或重新赋值)时,引用计数减少。当引用计数变为 0 时,说明没有任何std::shared_ptr
指向该对象了,此时该对象会自动被销毁。 - 示例用法:
#include <memory>class MyClass { public:~MyClass() {// 析构函数逻辑} };int main() {std::shared_ptr<MyClass> ptr1(new MyClass);std::shared_ptr<MyClass> ptr2 = ptr1;// 此时 ptr1 和 ptr2 都指向同一个 MyClass 对象,引用计数为 2// 当 ptr1 或 ptr2 其中一个离开作用域或者被重新赋值等操作导致不再指向该对象时,引用计数会相应减少// 只有当引用计数变为 0 时,MyClass 对象才会自动被析构return 0; }
std::weak_ptr
:- 弱引用:
std::weak_ptr
是一种不控制对象生命周期的智能指针,它主要用于配合std::shared_ptr
使用。它可以指向由std::shared_ptr
管理的对象,但不会增加该对象的引用计数。这使得它可以用来检测所指向的对象是否已经被销毁(通过调用lock
方法,如果返回的std::shared_ptr
为空,则说明对象已不存在)。 - 示例用法:
#include <memory>class MyClass { public:~MyClass() {// 析构函数逻辑} };int main() {std::shared_ptr<MyClass> ptr1(new MyClass);std::weak_ptr<MyClass> ptr2 = ptr1;// ptr2 是弱引用,不会增加 ptr1 指向的 MyClass 对象的引用计数if (auto locked_ptr = ptr2.lock(); locked_ptr) {// 如果能通过 lock 方法获取到有效的 std::shared_ptr,说明对象还存在,可以进行相应操作} else {// 说明对象已经被销毁}return 0; }
- 弱引用:
- 共享所有权:
- 独占所有权: