智能指针shared_ptr的应用场景

本文探讨了C++中智能指针shared_ptr的应用场景,包括多个使用者共享对象、避免复制昂贵的对象、以及管理特殊资源等。通过实例展示了如何使用shared_ptr简化容器中对象的管理和释放。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

智能指针shared_ptr的应用场景

既然智能指针shared_ptr能够帮助我们对程序中的内存资源进行很好的管理,避免内存泄漏或者内存访问错误的发生,那么我们能不能在任何时候都使用它来管理程序中的内存资源呢?

不能。shared_ptr增加了额外的引用计数而牺牲了一定的性能。

C++ 智能指针底层是采用引用计数的方式实现的。简单的理解,智能指针在申请堆内存空间的同时,会为其配备一个整形值(初始值为 1),每当有新对象使用此堆内存时,该整形值 +1;反之,每当使用此堆内存的对象被释放时,该整形值减 1。当堆空间对应的整形值为 0 时,即表明不再有对象使用它,该堆空间就会被释放掉。

总结起来,当出现以下情况时应该优先考虑使用shared_ptr来管理内存资源:

  1. 有多个使用者共同使用同一个对象,而这个对象没有一个明确的拥有者;
    这就好比教室里的电灯,大家都在使用这个电灯,但没有那个人来专门负责这个电灯的开关。往往是教室里一个人都没有,电灯却明晃晃地亮着。在这种场景下,我们就可以使用shared_ptr来管理这个电灯,通过shared_ptr,谁都可以使用这个电灯,但谁最后使用电灯谁就负责最后关灯。

  2. 某一个对象的复制操作很费时;
    如果一个对象的复制操作很费时,同时我们又需要在函数间传递这个对象,我们往往会选择传递指向这个对象的指针来代替传递对象本身,以此来避免对象的复制操作。既然选择使用指针,那么使用shared_ptr是一个更好的选择,即起到了向函数传递对象的作用,又不用为释放对象操心。

  3. 要把指针存入标准库容器;
    不管容器中保存的是普通指针还是智能指针,在使用上,两者并无太大区别,使用智能指针的优越性主要体现在容器使用完毕后清空容器的操作上。如果容器中保存的是普通指针,当我们在清空某个容器时,先要释放容器中指针所指向的资源,然后才能清空这些指针本身。例如,在SalarySys类的析构函数中,我们需要清空vecEmp容器中保存的Employee*指针:

class SalarySys
{
// …
public:
~SalarySys()
  {
      // …
      // 首先,释放容器中普通指针所指向的资源
     for(Employee* p : m_vecEmp)
      {
        delete p; // 释放指针所指向的对象
      }
      // 然后,用clear()函数清空容器
      m_vecEmp.clear();
  }
// …
private:
 vector<Employee*> m_vecEmp; // 保存普通指针的容器
}

如果把这个vecEmp容器中的普通指针替换成相应的shared_ptr,这个过程会简单的多。我们只需要使用clear()函数清空容器中保存的shared_ptr,而随着shared_ptr的释放,它会自动释放它所管理的资源,而无需我们主动去释放:

class SalarySys
{
// …
public:
~SalarySys()
  { 
      // …
      // 用clear()函数释放容器中的shared_ptr
      // shared_ptr在释放的时候也会连带地释放它所管理的Employee对象
      m_vecEmp.clear();
  }
// …
private:
 vector<shared_ptr<Employee>> m_vecEmp; // 保存shared_ptr的容器
}
 
  1. 当管理需要特殊清除方式的资源时,可以通过定制shared_ptr的删除器来实现。
    参考C++11 shared_ptr智能指针中关于定制shared_ptr定制删除器的介绍。

转载自你好,C++(79)什么时候应该使用智能指针呢?12.2.3 智能指针shared_ptr的应用场景

### C++ 中 `std::shared_ptr` 的使用方法与解析 #### 1. 定义与基本功能 `std::shared_ptr` 是一种智能指针,属于 C++ 标准库 `<memory>` 头文件的一部分。它的主要作用是通过引用计数机制实现动态分配对象的自动管理[^2]。 - **所有权共享**:多个 `std::shared_ptr` 可以共同指向同一对象,每个实例都对该对象拥有一部分所有权。 - **控制块**:内部维护了一个控制块,记录当前对象的引用计数。当引用计数降为零时,对象会被自动释放并清理内存。 #### 2. 创建方式 以下是创建 `std::shared_ptr` 的常见方法: ```cpp #include <iostream> #include <memory> int main() { // 方法一:使用 make_shared 函数 (推荐) auto ptr1 = std::make_shared<int>(42); // 方法二:直接初始化 std::shared_ptr<int> ptr2(new int(10)); std::cout << *ptr1 << ", " << *ptr2 << std::endl; } ``` 上述代码展示了两种创建 `std::shared_ptr` 的方式。其中,`std::make_shared` 是更高效且安全的选择,因为它减少了两次内存分配操作[^1]。 --- #### 3. 拷贝与赋值 `std::shared_ptr` 支持拷贝和赋值操作,在这些过程中会更新引用计数。 ```cpp #include <iostream> #include <memory> int main() { std::shared_ptr<std::string> sptr1 = std::make_shared<std::string>("hello"); std::shared_ptr<std::string> sptr2; // 赋值操作 sptr2 = sptr1; std::cout << "sptr1 use count: " << sptr1.use_count() << "\n"; // 输出 2 std::cout << "sptr2 use count: " << sptr2.use_count() << "\n"; // 输出 2 return 0; } ``` 在此示例中,`use_count()` 方法返回当前对象的引用计数值。可以看到,经过赋值后,两个指针共享同一个对象的所有权。 --- #### 4. 自定义删除器 可以通过设置自定义删除器来改变默认的行为(即调用 `delete` 删除对象)。 ```cpp #include <iostream> #include <memory> void custom_deleter(int* p) { std::cout << "Custom deleter called\n"; delete p; } int main() { std::shared_ptr<int> sptr(new int(10), custom_deleter); // 当 sptr 超出范围时,custom_deleter 将被执行 } ``` 此代码片段演示了如何指定一个函数作为删除器,从而在对象销毁时执行特定逻辑。 --- #### 5. 应用场景分析 下面是一个基于 `std::shared_ptr` 构建 N 叉树节点的例子,类似于 Trie 结构的应用[^3]。 ```cpp #include <iostream> #include <vector> #include <memory> #include <unordered_map> using namespace std; // 定义 N 叉树节点 struct Node { char value; bool isEndOfWord; unordered_map<char, shared_ptr<Node>> children; Node(char c): value(c), isEndOfWord(false) {} }; class Trie { public: shared_ptr<Node> root; Trie(): root(make_shared<Node>(' ')) {} void insert(const string& word) { shared_ptr<Node> current = root; for (char c : word) { if (!current->children.count(c)) current->children[c] = make_shared<Node>(c); current = current->children[c]; } current->isEndOfWord = true; } bool search(const string& word) const { shared_ptr<Node> node = findNode(word); return node && node->isEndOfWord; } private: shared_ptr<Node> findNode(const string& prefix) const { shared_ptr<Node> current = root; for (char c : prefix) { if (!current->children.count(c)) return nullptr; current = current->children[c]; } return current; } }; ``` 在这个例子中,`std::shared_ptr` 确保了即使某些分支不再被访问,也不会发生内存泄漏。 --- #### 总结 `std::shared_ptr` 提供了一种方便的方式来进行动态内存管理,尤其适合多线程环境下的资源共享场景。然而需要注意的是,过度依赖可能会引发循环引用等问题,因此应谨慎设计数据结构。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值