C++11智能指针详解:shared_ptr的使用与管理

版权申诉
0 下载量 23 浏览量 更新于2024-08-26 收藏 176KB PDF 举报
"C++11引入的智能指针是现代C++编程中管理动态内存的重要工具,它们能够自动处理对象的生命周期,防止内存泄漏和悬挂指针等问题。本资源主要探讨了C++11中的四种智能指针:auto_ptr、unique_ptr、shared_ptr和weak_ptr,其中auto_ptr在C++11中已被弃用。 智能指针的设计目标是替代原始指针,解决手动管理内存的问题。在C++中,堆内存的分配和释放由程序员负责,而智能指针通过内置的引用计数机制自动完成这一任务。 1. shared_ptr(共享智能指针) shared_ptr是多线程环境下常用的智能指针,它允许多个shared_ptr实例指向同一对象。内部有一个引用计数,每次复制或赋值时,计数器增加;当最后一个shared_ptr析构时,计数器减至0,对象随之被销毁。`use_count()`函数可以查询当前对象的引用计数。 初始化shared_ptr时,推荐使用`make_shared`工厂函数,因为它在性能上优于直接使用new操作符。例如: ```cpp std::shared_ptr<int> p1 = std::make_shared<int>(100); ``` 避免将原始指针直接赋值给shared_ptr,因为这可能导致错误。获取原始指针应谨慎,如: ```cpp int* raw_ptr = p1.get(); ``` 如果在智能指针析构后使用raw_ptr,可能会导致未定义行为。 2. unique_ptr(唯一智能指针) unique_ptr确保对象拥有唯一的所有权,不支持拷贝,只支持移动语义。这使得unique_ptr适用于单线程环境中的资源管理,其性能通常优于shared_ptr。例如: ```cpp std::unique_ptr<int> uptr(new int(5)); ``` unique_ptr没有内置的引用计数,当unique_ptr销毁时,其所指向的对象也会被销毁。 3. weak_ptr(弱智能指针) weak_ptr是与shared_ptr配合使用的,它不拥有对象,但可以观察shared_ptr的生命周期。weak_ptr不会增加对象的引用计数,因此不会阻止对象被删除。在访问弱指针所指向的对象前,需要检查对象是否仍然存在。 4. auto_ptr(已弃用) 在C++11之前,auto_ptr是标准库的一部分,但在C++11中已被废弃,原因是它的行为在某些情况下不符合预期,特别是与拷贝和赋值操作有关的问题。 智能指针的关键特性是它们能够在适当的时候自动调用delete,确保了内存的安全释放。正确地使用智能指针可以显著提高代码的健壮性和可维护性。在编写C++11及更高版本的代码时,应当优先考虑使用unique_ptr和shared_ptr,以实现更安全和高效的内存管理。对于需要观察对象生命周期而不直接拥有所有权的情况,可以使用weak_ptr。
2023-02-27 上传
C++智能指针 智能指针 智能指针概念 C/C++ 语⾔最为⼈所诟病的特性之⼀就是存在内存泄露问题,因此后来的⼤多数语⾔都提供了内置内存分配与释放功能,有的甚⾄⼲脆对语 ⾔的使⽤者屏蔽了内存指针这⼀概念。这⾥不置贬褒,⼿动分配内存与⼿动释放内存有利也有弊,⾃动分配内存和⾃动释放内存亦如此,这 是两种不同的设计哲学。有⼈认为,内存如此重要的东西怎么能放⼼交给⽤户去管理呢?⽽另外⼀些⼈则认为,内存如此重要的东西怎么能 放⼼交给系统去管理呢?在 C/C++ 语⾔中,内存泄露的问题⼀直困扰着⼴⼤的开发者,因此各类库和⼯具的⼀直在努⼒尝试各种⽅法去检 测和避免内存泄露,如 boost,智能指针技术应运⽽⽣。 智能指针主要⽤于管理在堆上分配的内存,它将普通的指针封装为⼀个栈对象。当栈对象的⽣存周期结束后,会在析构函数中释放掉申请的 内存,从⽽防⽌内存泄漏。简要的说,智能指针利⽤了 C++ 的 RAII 机制,在智能指针对象作⽤域结束后,会⾃动做内存释放的相关操作, 不需要我们再⼿动去操作内存。 RAII是C++的发明者Bjarne Stroustrup提出的概念,RAII全称是"Resource Acquisition is Initialization",直译过来是"资源获取即初始化",也 就是说在构造函数中申请分配资源,在析构函数中释放资源。因为C++的语⾔机制保证了,当⼀个对象创建的时候,⾃动调⽤构造函数,当 对象超出作⽤域的时候会⾃动调⽤析构函数。所以,在RAII的指导下,我们应该使⽤类来管理资源,将资源和对象的⽣命周期绑定。 C++ 中有四种智能指针:auto_pt、unique_ptr、shared_ptr、weak_ptr 其中后三个是 C++11 ⽀持,第⼀个已经被 C++11 弃⽤且被 unique_prt 代替,不推荐使⽤。下⽂将对其逐个说明。 std::auto_ptr 在这个年代讨论 std::auto_ptr 不免有点让⼈怀疑是不是有点过时了,确实如此,随着 C++11 标准的出现(最新标准是 C++20),std::auto_ptr 已经被彻底放弃,取⽽代之是 std::unique_ptr。然⽽,之所以还向介绍⼀下 std::auto_ptr 的⽤法以及它的设计不⾜ 之处是想更多了解 C++ 语⾔中智能指针的发展过程,⼀项技术如果我们了解它过去的样⼦和发展的轨迹,我们就能更好地掌握它。 std::auto_ptr 的基本⽤法如下代码所⽰: #include <iostream> #include <memory> using namespace std; int main() { //初始化⽅式1 std::auto_ptr<int> ap1(new int(8)); //初始化⽅式2 std::auto_ptr<int> ap2; ap2.reset(new int(8)); cout << *ap1 << ", " << *ap2 << endl; return 0; } 输出: 8, 8 智能指针对象 ap1 和 ap2 均持有⼀个在堆上分配 int 对象,其值均是 8,这两块堆内存均可以在 ap1 和 ap2 释放时得到释放。这是 std::auto_ptr 的基本⽤法。 std::auto_ptr 真正让⼈容易误⽤的地⽅是其不常⽤的复制语义,即当复制⼀个 std::auto_ptr 对象时(拷贝复制或 operator= 复制),原对象 所持有的堆内存对象也会转移给复制出来的对象。⽰例代码如下: #include <memory> int main() { //测试拷贝构造 std::auto_ptr<int> ap1(new int(8)); std::auto_ptr<int> ap2(ap1); if (ap1.get() != NULL) { std::cout << "ap1 is not empty." << std::endl; } else { std::cout << "ap1 is empty." << std::endl; } if (ap2.get() != NULL) { std::cout << "ap2 is not empty." << std::endl; } else { std::cout << "ap2 is empty." << std::endl; } //测试赋值构造 std::auto_ptr<int> ap3(new int(8)); std::auto_ptr<int> ap4; ap4 = ap3; if (ap3.get() != NULL) { std::cout << "ap3 is not empty." << std::endl; }