c++的类相较于c的结构体来说,在创建与销毁时会调用构造函数与析构函数,因此有人提出来了一种天才般的想法来解决c++的内存泄露问题,即在析构函数中进行内存释放-----智能指针即使基于这一想法实现的
#include <iostream>
using namespace std;
template<class T>
class smart_ptr {
public:
smart_ptr(T* ptr)
:_ptr(ptr)
{}
~smart_ptr()
{
cout << "delete[]_ptr" << endl;
// 将delete放进析构函数 ,类的声明周期结束自动进行回收
delete[]_ptr;
}
T* Get() {
return _ptr;
}
private:
T* _ptr;
};
int main()
{
smart_ptr<int> sp = new int[100];
system("pause");
return 0;
}
系统提供的智能指针在头文件<memory>下
c++库里的几种智能指针
基于不同使用的环境,在构造与赋值处有不同的实现方式
- auto_ptr 用auto_ptr向auto_ptr赋值时,会使发生管理权转移,赋值方将变为空 (个人感觉设计的不太好,慎用!)
- unique_ptr 简单粗暴,禁止拷贝,禁止复制
- shared_ptr 允许拷贝赋值,内部使用引用计数,最常用,循环引用时会出问题
- weak_ptr 用来解决shared_ptr循环引用的问题,可有shared_ptr赋值,不会使引用计数增加
循环引用
现有一个双向链表如下图所示,已知智能指针node1与node2->next指向同一块内存,引用计数为2。智能指针node2与node1->prev指向同一块内存,引用计数为2,如果我要释放node1指向的空间,需要先让这块空间的引用计数为0,所以我需要在释放node1指向的空间之前先析构node2->next,也就是释放node2指向的空间,而同理,如果我要释放node2指向的空间,也要先释放node1,死循环,出现异常
解决方案为prev和next使用weak_ptr,因为这个智能指针不会增加引用计数
// 错误例子
class node {
public:
shared_ptr<node> prev;
shared_ptr<node> next;
int val;
~node() {
cout << "delect " << endl;
}
};
int main()
{
shared_ptr<node> n1(new node);
shared_ptr<node> n2(new node);
n1->next = n2;
n2->prev = n1;
return 0;
}
定制删除器
对于一个智能指针来说,他可能用来维护
- malloc
- new
- FILE*
- 线程锁
- ...
维护的对象不同显然析构时调用的对象也应该不同,最好的办法就是删除器有使用者自己提供
shared_ptr模拟实现
#pragma once
#include <iostream>
#include <atomic>
#include <functional>
namespace mystd {
template<class T>
class shared_ptr {
public:
shared_ptr(T* ptr)
:_ptr(ptr),_pcount(new std::atomic<int>(1))
{}
template<class D>
shared_ptr(T* ptr, D del)
: _ptr(ptr), _pcount(new std::atomic<int>(1)), _del(del)
{}
shared_ptr(const shared_ptr<T>& sp)
:_ptr(sp._ptr), _pcount(sp._pcount)
{
++(*_pcount);
}
void release() {
if (--(*_pcount));
else {
_del(_ptr);
delete _pcount;
}
}
~shared_ptr()
{
release();
}
int use_count() {
return *_pcount;
}
shared_ptr<T>& operator=(const shared_ptr<T>& sp) {
shared_ptr<T> tmp(sp);
swap(tmp);
return *this;
}
void swap(shared_ptr<T>& sp) {
std::swap(_ptr, sp._ptr);
std::swap(_pcount, sp._pcount);
}
T& operator*() {
return *_ptr;
}
T* operator->() {
return _ptr;
}
private:
T* _ptr;
std::atomic<int>* _pcount;
std::function<void(T*)> _del = [](void* ptr) {delete ptr; };
};
}