移动语义和拷贝语义的区别以及智能指针
移动语义和拷贝语义的区别
一、概念本质:
拷贝语义
拷贝语义是基于对象的复制操作。当一个对象被拷贝时,会创建一个新的对象,这个新对象的内容是原始对象的完全副本,这意味着新对象和原始对象在内存中有独立的存储空间,并且它们包含相同的值。例如,对于一个包含动态分配内存的类,拷贝语义会重新分配一块新的内存,并将原始对象内存中的数据逐字节地复制到新内存中。
移动语义
移动语义则是一种资源所有权的转移,它主要用于将一个对象通常是临时对象或即将被错毁的对象)的资源(如动态分配的内存文件句柄等)直接转移给另一个对象,而不是进行复制。在移动语义中,源对象在资源转移后通常会处于一种“空”或“无效”的状态,以避免资源的双重使用或错误释放。
二、实现方式:
拷贝语义 - 拷贝构造函数和拷贝赋值运算符
在 C++ 中,拷贝语义通过拷贝构造函数(类名(const 类名&))和拷贝赋值运算符(类名& operator=(const 类名&))来实现。
移动语义 - 移动构造函数和移动赋值运算符
移动语义通过移动构造函数(类名(类名&&))和移动赋值运算符(类名& operator=(类名&&))来实现。
三、性能影响
拷贝语义
对于包含大量资源(如大型数组、复杂对象等)的对象,拷贝语义可能会导致性能开销。因为它需要为新对象分配新的资源,并逐个复制资源中的数
据。例如,当拷贝一个包含大量元素的vector对象时,需要重新分配内存并复制每个元素,这可能会消耗大量的时间和内存空间。
移动语义
移动语义在性能上有很大的优势,特别是对于临时对象或不再需要的对象。它可以快速地将资源从一个对象转移到另一个对象,避免了资源的复制操作。例如,当一个函数返回一个包含大量数据的对象时,通过移动语义可以直接将对象的资源转移给调用者的对象,而不是复制这些数据,从而提高了程序的运行效率。
四、使用场景
拷贝语义
当需要一个对象的完全独立副本时,例如在函数参数传递过程中,如果函数不应该修改原始对象,并且需要在函数内部使用一个独立的副本进行操作,就使用拷贝语义。或者当对象的生命周期需要独立管理,并且每个副本都应该有自己完整的资源时,也使用拷贝语义。
移动语义
移动语义主要用于对象资源的高效转移,比如在函数返回对象时,为了避免不必要的资源复制:或者在对象的生命周期结束时,将其资源转移给其他对象继续使用。例如,在容器类(如vector)中,当重新分配内存或进行元素的移动操作时,移动语义可以有效地转移元素的资源,提高容器操作的性能。
C++ 中的智能指针
在 C++ 中,智能指针是一种用于管理动态分配对象的机制。它的主要目的是帮助程序员自动地管理对象的生命周期,避免内存泄漏(即动态分配的内存没有被正确释放)和悬空指针(指向已经被释放的内存的指针)等问题。智能指针本质上是一个类模板(因为智能指针需要能够管理不同类型的对象以及它的核心功能与所管理对象的具体类型是无关的),它的行为类似于普通指针,但在对象的生命周期管理上更加智能。
C++11 中引入了三种新的智能指针类型:
1.unique ptr
用法:
unique_ptr 是一种独占所有权的智能指针,即同一时间内只能有一个 unique_ptr 指向一个给定的对象。当 unique_ptr 被销毁时,它所指向的对象也会被自动删除。
unique_ptr<int> ptr(new int(10));
2.shared ptr
用法:
shared_ptr 是一种共享所有权的智能指针,即多个 shared_ptr 可以同时指向同一个对象,并且会自动跟踪引用计数。对象的最后一个 shared_ptr 被销毁时,该对象才会被删除。
引用计数机制:shared_ptr 的内部维护了一个引用计数。每当一个新的 shared_ptr 指向同一个对象时,引用计数就会加 1;当一个 shared_ptr 不再指向该对象(例如,通过 reset 操作或者自身被销毁)时,引用计数就会减 1;当引用计数变为 0 时,对象会被自动销毁。
shared_ptr<int> ptr1(new int(20));
shared_ptr<int> ptr2=ptr1;
3.weak ptr
用法:
weak_ptr 是一种不控制对象生命周期的智能指针,它是为了解决 shared_ptr 相互引用导致的循环引用问题而设计的。weak_ptr需要与 shared_ptr 一起使用,它可以从一个shared_ptr 或另一个 weak_ptr 创建,但它不会增加引用计数。
shared_ptr<int> sharedPtr(new int(30));
weak_ptr<int> weakPtr(sharedPtr);
区别:
unique_ptr 用于独占所有权的场景,只能有一个指针指向对象,适用于资源管理。
shared_ptr 用于共享所有权的场景,可以有多个指针指向同一个对象,适用于多个对象需要访问同一资源的情况。
weak_ptr 用于辅助 shared_ptr,解决循环引用问题,不控制对象的生命周期。