C++ std::shared_ptr智能指针详解(附带实例)
C++11 引入了 3 种智能指针,它们是 std::shared_ptr、std::unique_ptr 和 std::weak_ptr。这些智能指针提供了更安全和方便的内存管理方式,主要用于管理动态分配的对象,主要通过利用对象的析构操作来自动释放内存,以此来提高内存的安全性。
这些智能指针提供了比传统指针更安全和更方便的内存管理方式。它们采用了 RAII(Resource Acquisition Is Initialization,资源获取就是初始化)原则,可以在对象超出作用域时自动释放资源,不再需要手动进行内存的分配和释放操作,从而减少内存泄漏和悬空指针的风险。此外,它们还提供了更多功能,如自定义删除器、自定义析构函数等,以更好地满足不同的需求。
引用计数是一个用于追踪有多少个 std::shared_ptr 共享同一个对象的整数值。它用于确定何时销毁动态分配的对象。
每次创建一个新的std::shared_ptr指向同一个对象时,引用计数会递增。当std::shared_ptr被销毁时,引用计数会递减。只有当引用计数变为0时,std::shared_ptr才会删除动态分配的对象并释放其占用的内存。
引用计数的基本原理是,每个 std::shared_ptr 对象都有一个指向一个控制块的指针。控制块是一个结构体,包含了引用计数和指向动态分配对象的指针。在每次创建或销毁 std::shared_ptr 对象时,引用计数会相应地进行递增或递减。
当引用计数减少到 0 时,std::shared_ptr 会调用其析构函数来销毁动态分配的对象,并释放控制块的内存。这确保了动态分配对象的生存期与所有引用它的 std::shared_ptr 对象的生存期一致。
引用计数的使用使多个 std::shared_ptr 对象可以共享同一对象的所有权,而不会造成资源的重复释放或悬空指针的情况。它提供了一种方便和安全的资源管理方式,能够自动进行内存释放。
需要注意的是,在使用 std::shared_ptr 进行循环引用时,可能会出现资源泄漏的问题。为了避免循环引用造成的资源无法释放的情况,需要使用 std::weak_ptr来 打破循环引用。
使用 std::shared_ptr 需要包含头文件 <memory>。下面是一个使用 shared_ptr 的示例,代码如下:
在示例中,ptr1 和 ptr2 共享同一个 MyClass 对象。当 ptr1 和 ptr2 超出作用域时,它们都被销毁,MyClass 对象的析构函数会被调用,从而释放资源。
使用 std::shared_ptr 可以避免手动管理动态分配资源的麻烦,实现了自动化的资源管理,避免了内存泄漏和悬空指针的问题。
std::make_shared() 可以简化创建 shared_ptr 的过程,并提供了更高的性能和安全性:
下面是一个使用 std::make_shared 的示例,代码如下:
这些智能指针提供了比传统指针更安全和更方便的内存管理方式。它们采用了 RAII(Resource Acquisition Is Initialization,资源获取就是初始化)原则,可以在对象超出作用域时自动释放资源,不再需要手动进行内存的分配和释放操作,从而减少内存泄漏和悬空指针的风险。此外,它们还提供了更多功能,如自定义删除器、自定义析构函数等,以更好地满足不同的需求。
C++ std::shared_ptr的用法
std::shared_ptr 是 C++11 中的智能指针,用于管理动态分配的对象资源,以自动进行资源释放。它是一个引用计数智能指针,可以让多个 shared_ptr 对象共享同一个资源,在最后一个 shared_ptr 被销毁时释放资源。引用计数是一个用于追踪有多少个 std::shared_ptr 共享同一个对象的整数值。它用于确定何时销毁动态分配的对象。
每次创建一个新的std::shared_ptr指向同一个对象时,引用计数会递增。当std::shared_ptr被销毁时,引用计数会递减。只有当引用计数变为0时,std::shared_ptr才会删除动态分配的对象并释放其占用的内存。
引用计数的基本原理是,每个 std::shared_ptr 对象都有一个指向一个控制块的指针。控制块是一个结构体,包含了引用计数和指向动态分配对象的指针。在每次创建或销毁 std::shared_ptr 对象时,引用计数会相应地进行递增或递减。
当引用计数减少到 0 时,std::shared_ptr 会调用其析构函数来销毁动态分配的对象,并释放控制块的内存。这确保了动态分配对象的生存期与所有引用它的 std::shared_ptr 对象的生存期一致。
引用计数的使用使多个 std::shared_ptr 对象可以共享同一对象的所有权,而不会造成资源的重复释放或悬空指针的情况。它提供了一种方便和安全的资源管理方式,能够自动进行内存释放。
需要注意的是,在使用 std::shared_ptr 进行循环引用时,可能会出现资源泄漏的问题。为了避免循环引用造成的资源无法释放的情况,需要使用 std::weak_ptr来 打破循环引用。
使用 std::shared_ptr 需要包含头文件 <memory>。下面是一个使用 shared_ptr 的示例,代码如下:
#include <memory> #include <iostream> class MyClass { public: MyClass() { std::cout << "MyClass Constructor" << std::endl; } ~MyClass() { std::cout << "MyClass Destructor" << std::endl; } void doSomething() { std::cout << "Doing something..." << std::endl; } }; int main() { std::shared_ptr<MyClass> ptr1(new MyClass()); //创建一个新的 MyClass 对象 //复制 ptr1,使 ptr2 和 ptr1 共享同一资源 std::shared_ptr<MyClass> ptr2 = ptr1; ptr1->doSomething(); //可以通过 ptr1 访问资源的成员函数 ptr2->doSomething(); //ptr1 和 ptr2 都指向同一资源 return 0; } //离开作用域时,ptr1 和 ptr2 都被销毁,资源自动释放执行结果为:
MyClass Constructor
Doing something...
Doing something...
MyClass Destructor
在示例中,ptr1 和 ptr2 共享同一个 MyClass 对象。当 ptr1 和 ptr2 超出作用域时,它们都被销毁,MyClass 对象的析构函数会被调用,从而释放资源。
使用 std::shared_ptr 可以避免手动管理动态分配资源的麻烦,实现了自动化的资源管理,避免了内存泄漏和悬空指针的问题。
C++ std::make_shared()的用法
C++ 提供了一个工厂函数模板 std::make_shared(),可以很方便地创建 std::shared_ptr 指针并将它初始化为指向动态分配的对象。std::make_shared() 可以简化创建 shared_ptr 的过程,并提供了更高的性能和安全性:
- 与直接使用 new 操作符创建 shared_ptr 对象相比,使用 std::make_shared() 能够有效地简化语法,可以减少代码的复杂性,并提高代码的可读性;
- std::make_shared 可以在一个内存块中同时分配对象和指向对象的控制块,减少了内存分配和管理的开销,提高程序性能;
- 在使用 new 创建 shared_ptr 时,如果在分配对象和分配控制块之间发生异常,则可能会造成资源泄漏,而 std::make_shared 会在一个原子操作中完成内存分配,并保证如果分配失败,则不会创建对象,避免了资源泄漏,提供了更强的异常安全性。
下面是一个使用 std::make_shared 的示例,代码如下:
#include <memory> #include <iostream> class MyClass { public: MyClass(int value) : m_value(value) { std::cout << "MyClass Constructor, value: " << m_value << std::endl; } ~MyClass() { std::cout << "MyClass Destructor, value: " << m_value << std::endl; } void doSomething() { std::cout << "Doing something with value: " << m_value << std::endl; } private: int m_value; }; int main() { //使用 std::make_shared<>() 构造对象 std::shared_ptr<MyClass> ptr = std::make_shared<MyClass>(42); ptr->doSomething(); return 0; } //当离开作用域时,ptr 被销毁,MyClass 对象的析构函数会被调用,从而释放资源执行结果为:
MyClass Constructor, value: 42
Doing something with value: 42
MyClass Destructor, value: 42