1 引入
C++程序设计中使用堆内存是非常频繁的操作,堆内存的申请和释放都由程序员自己管理。管理是麻烦点(e.g. 手动释放等),但无伤大雅,勉强可以接受;但要命的是,容易出问题:
- 内存泄露:忘记手动释放 或 由于异常存在跳过释放等,都会导致内存泄漏;
- 悬挂指针:
- 二次释放:多次释放同一指针会导致程序崩溃。
为了解决该问题,C++ 11 引入智能指针概念使内存管理变得更为方便,且不易出错:
- 从较浅的层面看,智能指针是利用了一种叫做RAII(资源获取即初始化)的技术对普通的指针进行封装,这使得智能指针实质是一个对象,行为表现的却像一个指针;
- 智能指针的作用是防止忘记调用delete释放内存和程序异常的进入catch块忘记释放内存。另外指针的释放时机也是非常有考究的,多次释放同一个指针会造成程序崩溃,这些都可以通过智能指针来解决。
智能指针包含在头文件<memory>中,包括:shared_ptr、unique_ptr、weak_ptr。
2 shared_ptr
对于一个对象,允许有多个shared_ptr指向该对象,并使用引用计数方式统计指向该对象的shared_ptr个数。
- 每多一个shared_ptr指向该对象,引用计数加一;每少一个shared_ptr指向该对象,引用计数减一;当引用计数为0时,自动删除所指向的堆内存;
- 注意不要用一个原始指针初始化多个shared_ptr,否则会造成二次释放同一内存;
- shared_ptr的一个最大的陷阱是循环引用,循环,循环引用会导致堆内存无法正确释放,导致内存泄漏;
#include <iostream>
#include <memory>
using namespace std;
void testShared() {
shared_ptr<int> sptrL1(new int);
shared_ptr<int> sptrL2(sptrL1); //拷贝构造1方式添加一个指向该对象的shared_ptr
shared_ptr<int> sptrL3 = sptrL2; //拷贝构造2方式添加一个指向该对象的shared_ptr
shared_ptr<int> sptrL4; //声明一个未指向任何对象的空shared_ptr
sptrL4 = sptrL3; //赋值方式添加一个指向该对象的shared_ptr
//输出引用计数个数,即:有多少个shared_ptr指向该对象
cout << sptrL1.use_count() << endl;
cout << sptrL2.use_count() << endl;
cout << sptrL3.use_count() << endl;
cout << sptrL4.use_count() << endl;
//获取原始指针
cout << sptrL1.get() << endl;
cout << sptrL2.get() << endl;
cout << sptrL3.get() << endl;
cout << sptrL4.get() << endl;
//使用make_shared方式初始化shared_ptr
shared_ptr<int> sptrR1 = make_shared<int>(10);
cout << sptrR1.use_count() << endl;
//使sptrL4指向sptrR1所指的对象
sptrL4 = sptrR1;
cout << sptrL1.use_count() << endl;
cout << sptrR1.use_count() << endl;
}
int main()
{
testShared();
system("pause");
return 0;
}
//输出结果
4
4
4
4
012AC768
012AC768
012AC768
012AC768
1
3
2
3 unique_ptr
对于一个对象,unique_ptr “唯一” 拥有该对象(i.g. 同一时刻只能有一个unique_ptr指向该对象),禁止通过拷贝构造,赋值等方式来使其它unique_ptr也指向该对象。
- unique_ptr 的生命周期:从创建开始,到其离开作用域;
- 当 unique_ptr 离开其作用域时,若其指向某个对象,则将该对象销毁(默认使用delete操作符,用户可指定其它操作);
- 在 unique_ptr 生命周期内,可以改变智能指针所指对象,如创建智能指针时通过构造函数指定、通过reset方法重新指定、通过release方法释放所有权,通过move方法转移所有权等。
#include <iostream>
#include <memory>
using namespace std;
void testUnique() {
unique_ptr<int> uptr1(new int);
//unique_ptr<int> uptr2(uptr1); //不允许以拷贝构造1方式使其它unique_ptr指向该对象
//unique_ptr<int> uptr2 = uptr1; //不允许以拷贝构造2方式使其它unique_ptr指向该对象
//unique_ptr<int> uptr2;
//uptr2 = uptr1; //不允许以赋值方式使其它unique_ptr指向该对象
cout << uptr1.get() << endl;
uptr1.reset(new int); //指向新的对象
cout << uptr1.get() << endl;
uptr1.release(); //释放所有权
cout << uptr1.get() << endl;
//使用make_unique方式初始化unique_ptr
unique_ptr<int> uptr2 = make_unique<int>(11);
cout << uptr2.get() << endl;
uptr1 = move(uptr2); //转移所有权
cout << uptr1.get() << endl;
cout << uptr2.get() << endl;
shared_ptr<int> sptr = move(uptr1); //所有权转移给shared_ptr
cout << sptr.get() << endl;
cout << uptr1.get() << endl;
}
int main()
{
testUnique();
system("pause");
return 0;
}
//输出结果
0124DF58
0124DF88
00000000
0124DF58
0124DF58
00000000
0124DF58
00000000
4 weak_ptr
weak_ptr是为了配合shared_ptr而引入的一种智能指针,它不具有普通指针的行为(i.g. 没有重载 operator* 和 -> ),它的最大作用在于协助shared_ptr工作,像旁观者那样观测资源的使用情况。
- weak_ptr可以从一个shared_ptr或者另一个weak_ptr对象构造,获得资源的观测权;但weak_ptr没有共享资源,它的构造不会引起指针引用计数的增加。
- 成员函数 use_count() 用于观测资源的引用计数;
- 成员函数 expired() 用于判断资源(被 weak_ptr 观测的资源 或 被 shared_ptr 管理的资源)是否存在(等价于use_count()==0,但更快);
- 成员函数 lock() 用于从被观测的 shared_ptr 获得一个可用的shared_ptr对象, 从而操作资源;但当expired()==true的时候,lock() 函数将返回一个存储空指针的shared_ptr。
#include <iostream>
#include <memory>
using namespace std;
void testWeak() {
shared_ptr<int> sptr1 = make_shared<int>(10);
cout << sptr1.use_count() << endl; //引用计数个数
weak_ptr<int> wptr(sptr1);
cout << wptr.use_count() << endl; //观测到的引用计数个数
if (!wptr.expired()) {
shared_ptr<int> sptr2 = wptr.lock(); //添加一个新的指向观测资源的shared_ptr
cout << sptr1.use_count() << endl; //引用计数增加
cout << sptr2.use_count() << endl;
cout << wptr.use_count() << endl;
cout << *sptr1 << endl;
*sptr2 = 100; //使用新得到的shared_ptr修改资源值
cout << *sptr1 << endl;
}
}
int main()
{
testWeak();
system("pause");
return 0;
}
//输出结果
1
1
2
2
2
10
100
5 实现原理
#include <iostream>
using namespace std;
template<typename T>
class SmartPointer {
private:
T* _ptr;
size_t* _count; //可以可以
public:
SmartPointer(T* ptr = nullptr) :
_ptr(ptr) {
if (_ptr) {
_count = new size_t(1);
}
else {
_count = new size_t(0);
}
}
SmartPointer(const SmartPointer& ptr) {
if (this != &ptr) {
this->_ptr = ptr._ptr;
this->_count = ptr._count;
(*this->_count)++; //666
}
}
SmartPointer& operator=(const SmartPointer& ptr) {
if (this->_ptr == ptr._ptr) {
return *this;
}
if (this->_ptr) {
(*this->_count)--;
if (this->_count == 0) {
delete this->_ptr;
delete this->_count;
}
}
this->_ptr = ptr._ptr;
this->_count = ptr._count;
(*this->_count)++;
return *this;
}
T& operator*() {
assert(this->_ptr == nullptr);
return *(this->_ptr);
}
T* operator->() {
assert(this->_ptr == nullptr);
return this->_ptr;
}
~SmartPointer() {
(*this->_count)--;
if (*this->_count == 0) {
delete this->_ptr;
delete this->_count;
}
}
size_t use_count() {
return *this->_count;
}
};
void testPrinciple() {
SmartPointer<int> sp(new int(10));
SmartPointer<int> sp2(sp);
SmartPointer<int> sp3(new int(20));
sp2 = sp3;
std::cout << sp.use_count() << std::endl;
std::cout << sp3.use_count() << std::endl;
}
int main()
{
testPrinciple();
system("pause");
return 0;
}
参考文献
来源:https://blog.csdn.net/qq_24309981/article/details/88096085