(声明这是我自己的读书笔记,这个知识点还不了解,仅仅是记录自己看)
在C++中动态内存的管理是通过 new 在动态内存中为对象分配空间并返回一个指向该对象的指针; delete 接受一个动态对象的指针,销毁对象,并释放与之相关联的内存。使用动态内存的时候很容易出问题。
新标准提供了两种智能指针类型来管理动态对象:(它们可以自动负责释放所指向的对象)
shared_ptr 允许多个指针指向同一个对象;
unique_ptr 它独占所指向的对象。
还在一个名为 weak_ptr 的伴随类,它是一种弱引用,指向 shared_ptr所管理的对象。
它们三个都定义在头文件 memory 中。
标准库早版本包含了为一个名为 auto_ptr 的类 ,它具有 unique_ptr 的特性,但不是全部。虽然它仍然是标准库的一部分,但编写程序的时候应该使用 unique_ptr 。
shared_ptr:
智能指针也是模板,我们创建它的时候,必须提供它可以指向的类型,默认初始化的智能指针中保存着一个空指针。
shared_ptr <string> p1;
shared_ptr<list<int>> p2;
智能指针的使用方式和普通指针类似:
shared_ptr 和 unique_ptr 都支持的操作 | |
shared_ptr <T> sp | 空智能指针,可以指向类型为T的对象 |
unique_ptr <T> up | |
p | 将p用作一个条件判断,若p指向一个对象,则为真,否则为假 |
*p | 解引用p,获得它指向的对象 |
p -> mem | 等价于 (*p) . mem |
p.get() | 返回p中保存的指针 |
swap (q, p) | 交换p 和 q的指针 |
p . swap (q) |
下面是shared_ptr 独有的操作:
make_shared<T> (args) | 返回一个 shared_ptr ,指向一个动态分配的类型为T 的对象。使用 args 初始化此对象 |
shared_ptr <T>p(q) | p是 shared_ptr q的拷贝;这个操作会递增q中的计数器,q中的指针必须能转换为 T* |
p = q | p和q都是shared_ptr,所保存的指针必须能相互转换,这个操作会递增q的引用计数,会递减p的引用计数;若p的引用计数变为0,则将其管理的原内存释放 |
p.unique() | 若p.use_count()为1,返回true,否则返回false |
p.use_count() | 返回与p共享对象的智能指针数量 ,可能很慢,主要用于调试 |
make_ptr函数:
最安全的分配和使用动态内存的 方法是调用make_shared标准库函数,它也被定义在头文件memory中。
shared_ptr<int> p3 = make_shared < int> (999); //指向一个值为42的int的 shared_ptr
shared_ptr<int> p4 = make_shared <int>(); //p4指向一个值初始化的Int,它的值为0
常用 auto 定义一个对象来保存 make_shared的结果比较简单。
auto p3 = make_shared < int> (999);
shared_ptr的拷贝与赋值:
当我们进行拷贝和赋值操作时,每个shared_ptr都会记录有多少个其他shared_ptr指向相同的对象:
auto p = make_shared < int> (999); //p指向的对象在这里只有一个引用者
auto q(p); //p和q指向相同的对象,这个对象有两个引用者
cout << *q << '\n' << *p << endl;//输出相同的值
每个shared_ptr 都有一个关联的计数器,通常称为 引用计数。一旦一个shared_ptr的计数器变为0,它就会释放自己所管理的对象;
auto r = make_shared<int>(520); //r指向的Int只有一个引用
r = q;//给r赋值,令它指向另一个地址;递增q指向的对象的引用计数;递减r原来指向的对象的引用计数; r原来指向的对象已经没有引用者,会自动释放
shared_ptr 自动销毁所管理的对象,它也会自动释放相关联的内存。
运算符new分配内存,delete释放new分配的内存。
动态分配的对象是默认初始化的;
int* p = new int(10);//new返回是一个指向该对象的指针
delete p;
一个动态分配的const对象必须进行初始化,分配的对象是const的,new返回的指针是一个指向const的指针。
当new不能分配我们所要求的内存空间,它会抛出一个类型为 bad_alloc 的异常。
int* p1 = new int; //如果分配失败,new抛出 std::bad_alloc
int* p2 = new (nothrow) int; //如果分配失败,new返回一个空指针
我们可以改变使用new的方式来阻止它抛出异常:这种形式称为 定位new , 我们将 nothrow 传递给 new,我们意图是告诉它不能抛出异常。这种形式的new不能分配内存,它会返回一个空指针。
bad_alloc 和 nothrow 都定义在头文件 new 中。
动态内存的管理非常容易出错:
1.忘记 delete 内存。
2.使用已经释放掉的对象。
3.同一块内存释放两次。
我们在 delete 之后应该重置指针给指针赋值为 nullptr;清楚的指出指针不指向任何对象。
shared_ptr 和 new 结合使用:我们可以使用New返回一指针来初始化智能指针。
shared_ptr <string> p1 ( new string ("hello")) ;//p1指向一个值为 hello 的string
接受指针参数的智能指针构造函数是 explicit(这种构造函数只能用于直接初始化) 的,必须使用直接初始化:(不能进行内置指针到智能指针的隐式转换)
shared_ptr<int> p1 = new int(1024);//错误,必须使用直接初始化
shared_ptr<int> p1(new int(999));//正确
默认情况下,一个用来初始化智能指针的普通指针必须指动态内存,因为智能指针默认使用 delete 释放它所关联的对象。我们要可以将智能指针绑定到一个指向其他类型的资源的指针上,为了这样做,我们必须提供自己的操作来替代 delete进行释放。
定义和改变 shared_ptr 的其他方法:
shared_ptr <T> p(q) | p管理内置指针q所指的对象;q必须指向 new 分配的内存,且能够转换为 T* 类型 |
shared_ptr<T> p(u) | p从 unique_ptr u 那里接管了对象的所有权;将u置为空 |
shared_ptr <T> p(q,d) | p接管了内置指针q所指向的对象的所有权。q必须能转换为T*类型。p将使用可调用对象d来代替 delete |
shared_ptr<T> p(p2,d) | p是shared_ptr p2的拷贝,唯一的区别是P将用可调用对象d来代替 delete |
p.reset() | 若p是唯一指向其对象的 shared_ptr , reset 会释此对象。若传递了可选的参数内置指针q ,会令P指向 q ;否则会将p 置为空。若还传递了参数 d ,将会调用d而不是 delete来释放 q |
p. reset(q) | |
p.resrt(q,d) |
不要混合使用普通指针和智能指针。
智能指针类型定义了一个名为get的函数,它返回一个内置指针,指向智能指针管理的对象。不要使用get 初始化另一个智能指针或为智能指赋值
使用智能指针的一些基本规范:
不使用相同的内置指针值初始化(或reser)多个智能指针。
不 delete get()返回的指针。
不使用 get() 初始化或 reset 另一个智能指针。
如果使用 get() 返回的指针 ,当最后一个对应的智能指针销毁后,指针就变为无效了。
如果使用智能指针管理的资源不是 new 分配的内存,要传递给它一个删除器(我们自己定义的一个函数代替delete , 这个函数必须能够完成对shared_ptr中保存的指针进行释放的操作)。
unique_ptr
当我们定义一个 unique_ptr时,需要将它绑定到一个new返回的指针上,初始化 unique_ptr 必须采用直接初始化。
unique_ptr<T> u1 unique_ptr<T,D> u2 | 空unique_ptr,可以指向类型为T的对象。u1会使用delete来释放它的指针;u2会使用一个类型为D的可调用对象来释放它的指针 |
unique_ptr<T,D> u(d) | 空unique_ptr,指向类型为T的对象,用类型为D的对象d代替 delete |
u= nullptr | 释放u指向的对象,将u置为空 |
u.release() | u放弃对指针的控制权,返回指针,并将u置为空 |
u.reset() | 释放u指向的对象 |
u.reset(q) | 如果提供了内置指针q,令u指向这个对象;否则将u置为空 |
u.reset(nullptr) |
weak_ptr
weak_ptr 是一种不控制所指向对象生存周期的智能指针,它指向由一个shared_ptr 管理的对象。
wrak_ptr<T> w | 空 weak_ptr 可以指向类型为T 的对象 |
weak_ptr <T> w(sp) | 与shared_ptr sp 指向相同对象的 weak_ptr。T必须能转换为sp指向的类型 |
w=p | p可以是一个shared_ptr或一个 weak_ptr。赋值后w与p共享对象 |
w.reset() | 将w置为空 |
w.use_count() | 与w共享对象的shared_ptr的数量 |
w.expired() | 若w.use_count()为0,返回true,否则返回false |
w.lock() | 如果expired 为true , 返回一个空 shared_ptr; 否则 返回一个指向w的对象的shared_ptr |
当我们创建一个weak_ptr时,要用一个 shared_ptr来初始化它:
auto p = make_shared<int>(42);
weak_ptr<int> wp(p);//wp弱共享p;p的引用计数没有改变
cout<<*(wp.lock());
我们不能使用weak_ptr直接访问对象,必须调用 lock。它返回一个指向共享对象的shared_ptr。
以上内容来自《C++ primer》书中知识点读书总结。