目录
- 指针
- 标准库
- unique_ptr
- 对应类方法
- 创建unique_ptr对象的两种方法
- unique_ptr的使用
- 特性
- shared_ptr
- 类方法
- 循环引用造成内存泄漏
- weak_ptr
- weak_ptr的创建
- 三个方法
- 功能
- 例子1
- 例子2
指针
- 普通指针:指向内存区域的地址变量
- 当普通指针指向动态分配的内存的时候,即使这个指针变量离开了所在的作用域,这块内存也不会被自动销毁,动态分配的内存不进行释放,则造成了内存泄漏。
- 如果一个指针指向了一块已经被释放的内存区域,那么这个指针就是悬空指针,使用悬空指针会造成不可预料的结果。
- 定义了一个指针,却未初始化使其指向有效的内存区域时,这个指针就成了野指针,使用野指针访问内存,一般会造成
segmentation fault
错误。
- 智能指针:封装了动态对象指针的类对象
使用智能指针,则可以有效的避免上述问题的发生。
智能指针是一个对象,它封装了指向另一个对象的指针,当智能指针离开作用域后,会被自动销毁,销毁过程中会调用析构函数来删除所封装的对象。
标准库
在标准模板库中提供了以下几种智能指针:
- unique_ptr
- shared_ptr
- weak_ptr
unique_ptr
在创建智能指针时,你可以传入一个指向对象的类型和自定义的删除器,(删除器一般为默认不传)。
Deleter
是一个模板类,它用于定义如何释放智能指针管理的对象,可以被作为删除器。
unique_ptr
与它所管理的动态对象是一对一的关系,不能有两个unique_ptr
同时指向同一个地址。
对应类方法
get()
— 用于获取所管理对象的指针- 重载的
->
成员运算符函数 — 调用了get
函数,也是返回了所管理对象的指针- 重载的
*
成员运算符 — 返回所管理对象的引用,相当于*get()
;release()
— 智能指针释放对该地址的所有权,返回一个原始指针,由调用者去负责后续该地址的内存管理。reset(T* newObject)
— 会删除原有的对象,接管新的对象。swap(unique_ptr<T>& other)
— 交换所管理的对象
创建unique_ptr对象的两种方法
ptr1 指向new出来的对象。
unique_ptr<A> ptr1(new A(参数))unique_ptr<A> ptr1 = make_unique<A>(参数)
unique_ptr的使用
- 当
unique_ptr
不再指向当前对象的时候,会自动删除对象。
#include <iostream>
using namespace std;class Rectangle {
public:Rectangle(double w, double h) :width(w),height(h){}~Rectangle() { cout << "对象被释放" << endl; }double area() { return width * height; }
private:double width;double height;
};int main()
{unique_ptr<Rectangle> pDemo(new Rectangle(3.5, 4.1));pDemo = nullptr;cout << "分隔-------分隔" << endl; //是否在这之前就将对象释放return 0;
}
特性
- 由于
unique_ptr
对所管理的资源具有独占性,所以unique_ptr
的特性有不能被拷贝,不能被赋值。 - 但可以通过
move
转移 - 一般智能指针的大小与指针是相同的
shared_ptr
多个shared_ptr
对象可以共同管理同一个指针。
它们通过一个共同的引用计数器来管理指针,例如,当有3个shared_ptr
都指向一个对象时,引用计数器的值为3,当一个智能指针被销毁时,引用计数器-1,当计数器为0时,会将所指向的内存对象释放。
类方法
use_count()
— 获得有多少个shared_ptr在共同管理同一个对象
unique()
— 返回use_count
是否等于1
循环引用造成内存泄漏
- 以下代码均有两个智能指针分别指向张三,李四,王五。
- 当people离开作用域被销毁后,会将每个
Person对象
的引用计数-1,但是每个Person对象
的成员partner智能指针
仍存在,所以无法自动删除person对象
,导致内存泄。
class Person
{
public:Person(const string& name) :_name(name) { cout << "构造函数调用" << endl; }~Person() { cout << _name << "销毁" << endl; }void setPartner(const shared_ptr<Person>& other) { _partner = other; }
private:string _name;shared_ptr<Person> _partner;};int main()
{vector<shared_ptr<Person>> people;people.push_back(shared_ptr<Person>(new Person("张三")));people.push_back(shared_ptr<Person>(new Person("李四")));people.push_back(shared_ptr<Person>(new Person("王五")));people[0]->setPartner(people[1]);people[1]->setPartner(people[2]);people[2]->setPartner(people[0]);return 0;
}
weak_ptr
用来表示临时所有权,不会增加引用计数,需要结合shared_ptr
使用。当需要临时所有权时,则将其转换为shared_ptr
,这样对象的引用计数会+1,来保证正在访问对象的有效性。
weak_ptr的创建
- 可以将一个
share_ptr
作为weak_ptr
的构造函数参数来初始化。 - 也可以直接将
share_ptr
赋值weak_ptr
。
三个方法
use_count()
— 返回引用计数expired()
— 用来检查与之关联的对象是否已经被销毁。如果对象已经被销毁(即shared_ptr
的引用计数为 0),那么expired()
返回true
;否则,返回false
。lock()
— 用于尝试将一个 weak_ptr 转换为shared_ptr
。如果weak_ptr
观察的对象仍然存在(即shared_ptr
的引用计数大于 0),那么lock()
会返回一个有效的shared_ptr
,指向相同的对象。如果对象已经被销毁(引用计数为 0),那么lock()
返回一个空的shared_ptr
。
功能
控制块是在share_ptr
第一次接管对象的时候创建的。
shared_ptr
决定 use_count
的值。
weak_ptr
决定 weak_count
的值。
当use_count
值等于0时,释放对象。
例子1
w_p1
不增加引用计数s_p1
,s_p2
指向对象rectangle
,所以计数为2。- 当退出作用域时,引用计数为0,释放对象。
例子2
- 创建临时所有权
- 当引用计数>0时,返回有效的
shared_ptr
- 当引用计数为0时,返回空