深入理解C++智能指针


  什么是智能指针?为什么需要这个东西?

  这需要从C++最早的设计说起,C++为了提高语言的自由程度,允许使用C++的程序员自己控制内存,C++继承了C语言的指针特性,允许用户在C++中像C一样使用指针在堆中开辟一块较大的内存,但是由于性能的考虑,用户自己申请的内存需要用户自己主动释放。这就导致了有时遗忘释放时带来的内存泄漏问题。由此智能指针为了解决这个问题,诞生了。

  C++的智能指针从C++11标准开始由boost引入, 关于boost的智能指针在此不再做介绍。

  以下讨论的内容涉及了C++11-C++20中的技术规则。

  在C++中使用智能指针需要包含<memory>头文件,智能指针作为STL的一部分来提供,所以在使用时需要使用std::命名空间,智能指针标准库提供了两个主要类:std::unique_ptr(私有指针)和std::shared_ptr(共享指针)。外加一个辅助类std::weak_ptr,下面一一介绍:

1.私有指针std::unique_ptr

  std::unique_ptr是一个模板类,所以在使用的时候需要输入一个类型参数,并且标准库提供了一个内存申请函数用来替代new。std::make_unique.

  

1 #include<memory>
2 
3 int main() {
4 
5     std::unique_ptr<int> p = std::make_unique<int>(12);
6 
7     // 类似于 int*p = new int(12);
8     return 0;
9 }

  当私有智能智能指针超出当前作用域时,智能指针将释放自己持有的内存。也就是说,上面的p指针,在main函数返回时,释放所持有的内存。智能指针也可以搭配new来完成内存的申请。

1 #include<memory>
2 
3 int main() {
4 
5 
6     std::unique_ptr<int> p2{ new int(13) };
7    
8     return 0;
9 }

此时,在p2超出作用域时,依然可以自动释放所持有的内存。

私有指针是不允许被复制的。

以下语法是错误的:

 1 #include<memory>
 2 
 3 int main() {
 4 
 5 
 6     std::unique_ptr<int> p2{ new int(13) };
 7 
 8     std::unique_ptr<int> m = p2;  // 错误,不允许被复制
 9    
10     return 0;
11 }

但是,可以被移动:

 1 #include<memory>
 2 
 3 int main() {
 4 
 5 
 6     std::unique_ptr<int> p2{ new int(13) };
 7 
 8     std::unique_ptr<int> m = std::move(p2);
 9    
10     return 0;
11 }

私有指针要求,每一块内存只能有一个对象持有。所以符合移动复制的都是合法的,符合拷贝复制的都是不合法的。

对于智能指针数组,标准库依然有一套特化来解决。

1 #include<memory>
2 
3 int main() {
4 
5 
6     std::unique_ptr<int[]> p = std::make_unique<int[]>(12);
7    
8     return 0;
9 }

上面的代码中表示,申请了一块数组长度为12 int类型的连续内存。数组指针也可以配合new来完成,以达到数组的初始化工作。

1 #include<memory>
2 
3 int main() {
4 
5 
6     std::unique_ptr<int[]> p{ new int[12]{12,23,45,67} };
7    
8     return 0;
9 }

2.共享指针std::shared_ptr

共享指针的使用方式和私有指针相似,但是共享指针是允许被复制的。

 1 #include<memory>
 2 
 3 int main() {
 4 
 5 
 6     std::shared_ptr<int> p = std::make_shared<int>(12);
 7 
 8     auto s = p;  // ok
 9    
10     return 0;
11 }

共享指针的释放条件是:当所有持有该内存的指针对象全部消亡以后,释放该内存。

对于智能指针访问内部数据的方法是,该对象重载了*和->操作符。

 1 #include<memory>
 2 
 3 int main() {
 4 
 5 
 6     std::shared_ptr<int> p = std::make_shared<int>(12);
 7 
 8     int m = *p;    // 读取p的内存值
 9 
10     int* s = p.get();  // 获取p的内存的地址
11    
12     return 0;
13 }

私有智能指针允许将内存管理权限交于程序员自己管理。

 1 #include<memory>
 2 
 3 int main() {
 4 
 5 
 6     auto p = std::make_unique<int>(12);
 7 
 8     int* s = p.release();   // 此时智能指针对象在消亡时,不再主动释放内存。
 9 
10     return 0;
11 }

共享指针由于存在可能被多个对象持有的情况,所以不支持内存权限转移。

3. std::waek_ptr

 1 #include<memory>
 2 
 3 int main() {
 4 
 5 
 6     auto p = std::make_shared<int>(12);
 7     
 8     std::weak_ptr<int> wk(p);
 9     if (!wk.lock()) {
10         // 对象已经释放。
11     }
12     wk.reset(); // 释放持有的智能指针对象。(注意:不是释放持有的智能指针的内存,而是释放持有的p对象的引用。)
13 
14     return 0;
15 }

std::waek_ptr在使用时的主要用途是用来判断一个共享指针是否已经释放,对于智能指针的原理,每个人都有自己的一套见解,对于智能指针的原理,这就涉及到源码解析,由于篇幅过大,在此不再细说。

有几点是需要注意的:

1.尽量避免智能指针与普通指针的混用。(你要是想骚,那也没办法)。

2.避免重写智能指针的内存管理类。(同上)。

3.在团队项目上应与团队成员商议,是否采用智能指针。

智能指针的出现,是为了更方便,而不是为了更骚,更复杂,一切都应该建立在:此方法能够让我更舒服的且高效的工作。

 

  

 


免责声明!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系本站邮箱yoyou2525@163.com删除。



 
粤ICP备18138465号  © 2018-2025 CODEPRJ.COM