目录
一、为什么需要智能指针?
二、智能指针的原理及使用
三、auto_ptr
3.1 - 基本使用
3.2 - 模拟实现
四、unique_ptr
4.1 - 基本使用
4.2 - 模拟实现
5.1 - 基本使用
5.2 - 模拟实现
六、weak_ptr
6.2 - 模拟实现
七、定制删除器
一、为什么需要智能指针?
问题引入:
#include <iostream>
using namespace std;
int division(int x, int y)
{
if (y == 0)
throw "Division by zero condition!";
else
return x / y;
}
void func()
{
string* p1 = new string("hello");
pair<string, string>* p2 = new pair<string, string>{ "hello", "你好" };
int a = 0, b = 0;
cin >> a >> b;
cout << division(a, b) << endl;
delete p1;
delete p2;
}
int main()
{
try {
func();
}
catch (const char* errmsg) {
cout << errmsg << endl;
}
catch (...) {
cout << "Unknow exception" << endl;
}
return 0;
}
调用 func 函数:
如果在执行
string* p1 = new string("hello");
语句的过程中抛出了异常,那么在 main 函数中会捕获到抛出的异常;如果在执行
pair<string, string>* p2 = new pair<string, string>{ "hello", "你好" };
语句的过程中抛出了异常,那么在 main 函数中会捕获到抛出的异常,但是 p1 指向的动态分配的内存没有被释放,最终会造成内存泄漏;如果在调用
division(a, b)
函数的过程中抛出了异常,那么在 main 函数中会捕获到抛出的异常,但是 p1 以及 p2 指向的动态分配的内存都没有被释放,最终也会造成内存泄漏。
二、智能指针的原理及使用
RAII(Resource Acquisition Is Initialization,资源获取即初始化)是一种利用对象生命周期来控制程序资源(如内存、文件句柄、网络连接、互斥量等等)的简单技术。
在对象构造时获取资源,对资源的访问在对象生命周期内始终有效;在对象析构时,即对象生命周期结束时,释放资源。
SmartPtr.h:
#pragma once
namespace yzz
{
template<class T>
class SmartPtr
{
public:
SmartPtr(T* ptr = nullptr) : _ptr(ptr)
{ }
~SmartPtr()
{
if (_ptr)
{
delete _ptr;
_ptr = nullptr;
}
}
T& operator*()
{
return *_ptr;
}
T* operator->()
{
return _ptr;
}
private:
T* _ptr;
};
}
test.cpp:
#include <iostream>
#include "SmartPtr.h"
using namespace std;
int division(int x, int y)
{
if (y == 0)
throw "Division by zero condition!";
else
return x / y;
}
void func()
{
yzz::SmartPtr<string> sp1 = new string("hello");
cout << *sp1 << endl;
yzz::SmartPtr<pair<string, string>> sp2 = new pair<string, string>{ "hello", "你好"};
cout << (*sp2).first << " : " << (*sp2).second << endl;
cout << sp2->first << " : " << sp2->second << endl;
// 为了可读性,编译器将 sp2->->first/second 优化成了 sp2->first/second
int a = 0, b = 0;
cin >> a >> b;
cout << division(a, b) << endl;
// 在 func 函数中,无论有没有异常抛出,
// func 函数结束后,都会自动调用 sp1 以及 sp2 对象的析构函数释放动态分配的内存,
// 不再需要手动 delete
}
int main()
{
try {
func();
}
catch (const char* errmsg) {
cout << errmsg << endl;
}
catch (...) {
cout << "Unknow exception" << endl;
}
return 0;
}
三、auto_ptr
C++98/03 标准中提供了 auto_ptr 智能指针。
3.1 - 基本使用
#include <memory>
#include <iostream>
using namespace std;
class A
{
public:
A(int x = 0) : _i(x)
{
cout << "A(int x = 0)" << endl;
}
~A()
{
cout << "~A()" << endl;
}
int _i;
};
int main()
{
auto_ptr<A> ap1(new A(1));
auto_ptr<A> ap2(new A(2));
auto_ptr<A> ap3(ap1); // 管理权转移
auto_ptr<A> ap4(new A(4));
ap4 = ap2; // 管理权转移
// cout << ap1->_i << endl; // error
// cout << ap2->_i << endl; // error
cout << ap3->_i << endl; // 1
cout << ap4->_i << endl; // 2
return 0;
}
3.2 - 模拟实现
namespace yzz
{
template<class T>
class auto_ptr
{
public:
auto_ptr(T* ptr = nullptr) : _ptr(ptr)
{ }
~auto_ptr()
{
if (_ptr)
{
delete _ptr;
_ptr = nullptr;
}
}
auto_ptr(auto_ptr<T>& ap)
: _ptr(ap._ptr)
{
ap._ptr = nullptr;
}
auto_ptr<T>& operator=(auto_ptr<T>& ap)
{
if (this != &ap)
{
if (_ptr)
delete _ptr;
_ptr = ap._ptr;
ap._ptr = nullptr;
}
return *this;
}
T& operator*()
{
return *_ptr;
}
T* operator->()
{
return _ptr;
}
private:
T* _ptr;
};
}
四、unique_ptr
C++11 标准废弃了 auto_ptr,新增了 unique_ptr、shared_ptr 以及 weak_ptr 这三个智能指针。
4.1 - 基本使用
#include <memory>
#include <iostream>
using namespace std;
class A
{
public:
A(int x = 0) : _i(x)
{
cout << "A(int x = 0)" << endl;
}
~A()
{
cout << "~A()" << endl;
}
int _i;
};
int main()
{
unique_ptr<A> up1(new A(1));
unique_ptr<A> up2(new A(2));
// unique_ptr<A> up3(up1); // error
unique_ptr<A> up4(new A(4));
// up4 = up2; // error
return 0;
}
4.2 - 模拟实现
从上面的例子中可以看出,unique_ptr 的实现原理就是简单粗暴的防拷贝。
namespace yzz
{
template<class T>
class unique_ptr
{
public:
unique_ptr(T* ptr = nullptr) : _ptr(ptr)
{ }
~unique_ptr()
{
if (_ptr)
{
delete _ptr;
_ptr = nullptr;
}
}
unique_ptr(const unique_ptr<T>& up) = delete;
unique_ptr<T>& operator=(const unique_ptr<T>& up) = delete;
T& operator*()
{
return *_ptr;
}
T* operator->()
{
return _ptr;
}
private:
T* _ptr;
};
}
五、shared_ptr
5.1 - 基本使用
#include <memory>
#include <iostream>
using namespace std;
class A
{
public:
A(int x = 0) : _i(x)
{
cout << "A(int x = 0)" << endl;
}
~A()
{
cout << "~A()" << endl;
}
int _i;
};
int main()
{
shared_ptr<A> sp1(new A(1));
shared_ptr<A> sp2(new A(2));
cout << sp1.use_count() << endl; // 1
cout << sp2.use_count() << endl; // 1
cout << "-------------" << endl;
shared_ptr<A> sp3(sp1);
cout << sp1.use_count() << endl; // 2
cout << sp3.use_count() << endl; // 2
sp3->_i *= 10;
cout << sp1->_i << endl; // 10
cout << sp3->_i << endl; // 10
cout << "-------------" << endl;
shared_ptr<A> sp4(new A(4));
sp4 = sp2;
cout << sp2.use_count() << endl; // 2
cout << sp4.use_count() << endl; // 2
sp4->_i *= 10;
cout << sp2->_i << endl; // 20
cout << sp4->_i << endl; // 20
return 0;
}
5.2 - 模拟实现
shared_ptr 的实现原理:通过引用计数的方式实现多个 shared_ptr<T> 类对象之间共享资源。
引用计数:用来记录资源使用者的个数。在构造时,将资源的计数给成 1,每增加一个对象使用该资源,就给计数增加 1,当某个对象被销毁时,先给该计数减 1,然后再检查是否需要释放资源,如果计数为 0,说明该对象是资源的最后一个使用者,于是将资源释放,否则就不能释放,因为还有其他对象在使用该资源。
namespace yzz
{
template<class T>
class shared_ptr
{
public:
shared_ptr(T* ptr = nullptr)
: _ptr(ptr), _pCnt(new int(1))
{ }
~shared_ptr()
{
if (--(*_pCnt) == 0)
{
if (_ptr)
{
delete _ptr;
_ptr = nullptr;
}
delete _pCnt;
}
}
shared_ptr(const shared_ptr<T>& sp)
: _ptr(sp._ptr), _pCnt(sp._pCnt)
{
++(*_pCnt);
}
shared_ptr<T>& operator=(const shared_ptr<T>& sp)
{
if (_ptr != sp._ptr)
{
if (--(*_pCnt) == 0)
{
if (_ptr)
delete _ptr;
delete _pCnt;
}
_ptr = sp._ptr;
_pCnt = sp._pCnt;
++(*_pCnt);
}
return *this;
}
T& operator*()
{
return *_ptr;
}
T* operator->()
{
return _ptr;
}
int use_count() const
{
return *_pCnt;
}
T* get() const
{
return _ptr;
}
private:
T* _ptr;
int* _pCnt;
};
}
六、weak_ptr
6.1 - shared_ptr 的循环引用问题
#include <memory>
#include <iostream>
using namespace std;
class A
{
public:
A(int x = 0) : _i(x)
{
cout << "A(int x = 0)" << endl;
}
~A()
{
cout << "~A()" << endl;
}
int _i;
};
struct ListNode
{
A _val;
shared_ptr<ListNode> _prev;
shared_ptr<ListNode> _next;
};
int main()
{
shared_ptr<ListNode> sp1(new ListNode);
shared_ptr<ListNode> sp2(new ListNode);
// A(int x = 0)
// A(int x = 0)
cout << sp1.use_count() << endl; // 1
cout << sp2.use_count() << endl; // 1
sp1->_next = sp2;
sp2->_prev = sp1;
cout << sp1.use_count() << endl; // 2
cout << sp2.use_count() << endl; // 2
// 出现内存泄漏问题
return 0;
}
解决方案:
#include <memory>
#include <iostream>
using namespace std;
class A
{
public:
A(int x = 0) : _i(x)
{
cout << "A(int x = 0)" << endl;
}
~A()
{
cout << "~A()" << endl;
}
int _i;
};
struct ListNode
{
A _val;
// weak_ptr 不增加引用计数,并且在可以访问资源的同时,不参与释放资源
weak_ptr<ListNode> _prev;
weak_ptr<ListNode> _next;
};
int main()
{
shared_ptr<ListNode> sp1(new ListNode);
shared_ptr<ListNode> sp2(new ListNode);
// A(int x = 0)
// A(int x = 0)
cout << sp1.use_count() << endl; // 1
cout << sp2.use_count() << endl; // 1
sp1->_next = sp2;
sp2->_prev = sp1;
cout << sp1.use_count() << endl; // 1
cout << sp2.use_count() << endl; // 1
// ~A()
// ~A()
return 0;
}
6.2 - 模拟实现
namespace yzz
{
template<class T>
class weak_ptr
{
public:
weak_ptr() : _ptr(nullptr)
{ }
weak_ptr(const shared_ptr<T>& sp)
: _ptr(sp.get())
{ }
weak_ptr<T>& operator=(const shared_ptr<T>& sp)
{
_ptr = sp.get();
return *this;
}
T& operator*()
{
return *_ptr;
}
T* operator->()
{
return _ptr;
}
private:
T* _ptr;
};
}
七、定制删除器
template<class T>
struct DeleteArrayFunc
{
void operator()(T* ptr)
{
delete[] ptr;
}
};
template<class T>
struct FreeFunc
{
void operator()(T* ptr)
{
free(ptr);
}
};
int main()
{
shared_ptr<A> sp1(new A(1)); // ok
// shared_ptr<A> sp2(new A[5]); // error
shared_ptr<A> sp2(new A[5], DeleteArrayFunc<A>()); // ok
// shared_ptr<A> sp3((A*)malloc(sizeof(A))); // error
shared_ptr<A> sp3((A*)malloc(sizeof(A)), FreeFunc<A>()); // ok
// shared_ptr<FILE> sp4(fopen("test.cpp", "r")); // error
shared_ptr<FILE> sp4(fopen("test.cpp", "r"),
[](FILE* fp) { fclose(fp); }); // error
return 0;
}
shared_ptr 的模拟实现:
#include <functional>
namespace yzz
{
template<class T>
class shared_ptr
{
public:
shared_ptr(T* ptr = nullptr)
: _ptr(ptr), _pCnt(new int(1)), _del([](T* ptr) { delete ptr; })
{ }
template<class D>
shared_ptr(T* ptr, D del)
: _ptr(ptr), _pCnt(new int(1)), _del(del)
{ }
~shared_ptr()
{
if (--(*_pCnt) == 0)
{
if (_ptr)
{
_del(_ptr);
_ptr = nullptr;
}
delete _pCnt;
}
}
// ... ...
private:
T* _ptr;
int* _pCnt;
std::function<void(T*)> _del;
};
}