智能指针是为了解决内存泄漏的问题。
在C语言中,我们用malloc申请内存,free释放内存;在C++中,既可以使用上述,对于自定义类型常常会用new申请,delete释放。一旦申请,忘了释放,就会导致内存泄漏。
即便有的时候,delete了,但是由于中间某些操作会异常,导致无法delete,也会造成内存泄漏。
所以引入了智能指针,能最大程度避免内存泄漏又能兼顾效率。
是由于疏忽或错误导致程序未能释放已经不再使用的内存的情况。并不是指内存在物理上的消失,而是在应用程序分配某段内存后,因为设计错误,而失去了对该段内存的控制,因而造成了内存泄漏。
长期运行的程序出现内存泄漏,如操作系统、后台服务等,会导致响应越来越慢,最终卡死。
堆内存泄漏指的是通过malloc / calloc / realloc / new 等从堆中分配的一块内存,在使用完成以后,没有使用free / delete 删除。假设因为程序的设计错误导致这块内存没有被删掉,就会导致以后无法使用,就会造成堆内存泄漏。
指程序使用系统分配的资源,如套接字、文件描述符、管道没有使用对应的函数释放,导致系统资源的浪费,严重可导致系统效能减少,系统执行不稳定。
是指程序在申请新的内存空间后,没有释放已经申请的内存空间,后果也许会造成内存溢出。
指程序申请内存时,没有足够的内存提供给申请者。内存不够用。
内存溢出的原因及解决
内存加载的数据量过于庞大,如一次从数据库取出过多数据。
代码中存在死循环或者循环过多重复的实体对象
使用第三方软件中的BUG
启动参数内存值设定的过小
检查代码中是否有死循环或递归调用
检查是否有大循环重复产生新对象实体
检查对数据库查询中,是否有一次性获取全部数据。
智能指针的作用:可以帮助我们避免在申请内存空间后忘记释放造成内存泄漏的问题。因为智能指针本身是一个类,当出了类的作用域,类会调用析构函数进行释放资源。所以智能指针的作用原理就是在函数结束时自动释放内存空间。
(Resource Acquisition Is Initialization)资源获取即初始化。
在类的构造函数中申请资源并使用,最后在析构函数中释放资源。
// 使用RAII思想设计的SmartPtr类
template<class T>
class SmartPtr {
public:
SmartPtr(T* ptr = nullptr)
: _ptr(ptr)
{}
~SmartPtr()
{
if(_ptr)
delete _ptr;
}
private:
T* _ptr;
};
void MergeSort(int* a, int n)
{
int* tmp = (int*)malloc(sizeof(int)*n);
// 讲tmp指针委托给了sp对象
SmartPtr<int> sp(tmp);
// _MergeSort(a, 0, n - 1, tmp);
// 这里假设处理了一些其他逻辑
vector<int> v(1000000000, 10);
// ...
}
int main()
{
try {
int a[5] = { 4, 5, 2, 3, 1 };
MergeSort(a, 5);
}
catch(const exception& e)
{
cout<<e.what()<<endl;
}
return 0;
}
上述的smartptr还不能将其成为智能指针,因为还不具备指针的行为。指针可以解引用,也可以通过 -> 去访问所知空间的内容。所以需要对* 、-> 进行重载。
template<class T>
class SmartPtr {
public:
SmartPtr(T* ptr = nullptr)
: _ptr(ptr)
{}
~SmartPtr()
{
if(_ptr)
delete _ptr;
}
T& operator*() {return *_ptr;}
T* operator->() {return _ptr;}
private:
T* _ptr;
};
struct Date
{
int _year;
int _month;
int _day;
};
int main()
{
SmartPtr<int> sp1(new int);
*sp1 = 10
cout<<*sp1<<endl;
SmartPtr<int> sparray(new Date);
// 需要注意的是这里应该是sparray.operator->()->_year = 2018;
// 本来应该是sparray->->_year这里语法上为了可读性,省略了一个->
sparray->_year = 2018;
sparray->_month = 1;
sparray->_day = 1;
}
总结原理:
RAII特性 + 重载operator* 和 operator -> ,具有像指针一样的行为。
包含头文件:
#include < memory>
这里的指针主要指的是shared_ptr
,是一种引用计数型智能指针。可以记录内存中有多少个智能指针被引用,新增一个引用计数+ 1,过期一个引用计数 - 1,当引用计数为 0 的时候 , 智能指针才会释放资源。
通过引用计数的方式实现多个shared_ptr对象之间的共享资源。
#include <iostream>
#include <memory>
using namespace std;
class A
{
public:
A(int count)
{
_nCount = count;
}
~A(){}
void Print()
{
cout<<"count:"<<_nCount<<endl;
}
private:
int _nCount;
};
int main()
{
shared_ptr<A>p(new A(10));//初始化
p->Print();
return 0;
}
循环引用计数:两个智能指针互相指向对方,造成内存泄漏。需要weak_ptr,将其中的一个指针设置为weak_ptr。
因为weak_ptr没有共享资源,它的构造函数不会引起智能指针引用计数的变化。
对于 shared_ptr, 多个对象需要共用一个引用计数变量, 所以会存在线程安全问题. 但是标准库实现的时候考虑到了这个问题, 基于原子操作(CAS)的方式保证 shared_ptr 能够高效, 原子的操作引用计数
#include<iostream>
#include<momory>
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);
}
}
SmartPointer& opreater = (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 (0 == *this->_count)
{
delete this->ptr;
delete this->count;
}
}
size_t use_count()
{
return *this->_count;
}
};
int main()
{
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_cout() << std::endl;
}
独占式智能指针,不允许拷贝复制,不允许拷贝构造。
是线程安全的。由于只是在当前代码块范围内生效, 因此不涉及线程安全问题
弱引用指针,用于观察shared_ptr或weak_ptr。用于解决循环引用 计数。
因为weak_ptr没有共享资源,它的构造函数不会引起智能指针引用计数的变化。