目录
指针
- 普通指针:指向内存区域的地址变量
- 当普通指针指向动态分配的内存的时候,即使这个指针变量离开了所在的作用域,这块内存也不会被自动销毁,动态分配的内存不进行释放,则造成了内存泄漏。
- 如果一个指针指向了一块已经被释放的内存区域,那么这个指针就是悬空指针,使用悬空指针会造成不可预料的结果。
- 定义了一个指针,却未初始化使其指向有效的内存区域时,这个指针就成了野指针,使用野指针访问内存,一般会造成
segmentation fault
错误。
- 智能指针:封装了动态对象指针的类对象
使用智能指针,则可以有效的避免上述问题的发生。
智能指针是一个对象,它封装了指向另一个对象的指针,当智能指针离开作用域后,会被自动销毁,销毁过程中会调用析构函数来删除所封装的对象。
标准库
在标准模板库中提供了以下几种智能指针:
- unique_ptr
- shared_ptr
- weak_ptr
unique_ptr
在创建智能指针时,你可以传入一个指向对象的类型和自定义的删除器,(删除器一般为默认不传)。
Deleter 是一个模板类,它用于定义如何释放智能指针管理的对象,可以被作为删除器。
unique_ptr与它所管理的动态对象是一对一的关系,不能有两个unique_ptr同时指向同一个地址。
对应类方法
get()
— 用于获取所管理对象的指针- 重载的
->
成员运算符函数 — 调用了get
函数,也是返回了所管理对象的指针- 重载的
*
成员运算符 — 返回所管理对象的引用,相当于*get()
;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时,返回空