文章目录
1.代码问题与初步解决
int div()
{
int a, b;
cin >> a >> b;
if (b == 0)
throw invalid_argument("除0错误");
return a / b;
}
void Func()
{
int* p1 = new int;
int* p2 = new int;
cout << div() << endl;
delete p1;
delete p2;
}
int main()
{
try
{
Func();
}
catch (exception& e)
{
cout << e.what() << endl;
}
return 0;
}
很明显 若p2抛异常那么需要释放p1 若div函数抛异常 那么需要释放p1 p2 …怎么改进代码?
int div()
{
int a, b;
cin >> a >> b;
if (b == 0)
throw invalid_argument("除0错误");
return a / b;
}
void Func()
{
int* p1 = new int;
int* p2 = nullptr;
try
{
p2 = new int;
}
catch (...)
{
delete p1;
throw;
}
try
{
cout << div() << endl;
}
catch (...)
{
delete p1;
delete p2;
throw;
}
delete p1;
delete p2;
}
int main()
{
try
{
Func();
}
catch (exception& e)
{
cout << e.what() << endl;
}
return 0;
}
如果后续还有更多 那么就需要我们一个一个搞 怎么办???
2.内存泄漏相关知识
2.1什么是内存泄漏
内存泄漏:因为疏忽或错误造成程序未能释放已不再使用的内存的情况 内存泄漏并不是指内存在物理上的消失,而是应用程序分配某段内存后,因为设计错误,失去了对该段内存的控制,因而造成了内存的浪费。
void MemoryLeaks()
{
// 1.内存申请了忘记释放
int* p1 = (int*)malloc(sizeof(int));
int* p2 = new int;
// 2.异常安全问题
int* p3 = new int[10];
Func(); // Func函数抛异常导致 delete[] p3未执行 p3没被释放.
delete[] p3;
}
危害:长期运行的程序出现内存泄漏,影响很大,如操作系统、后台服务等,出现内存泄漏会导致响应越来越慢,最终卡死。
2.2内存泄漏分类
- 堆内存泄漏(Heap leak)
堆内存指的是程序执行中通过malloc / calloc / realloc / new等从堆中分配的一块内存,用完后必须通过调用相应的 free或者delete 释放。假设程序的设计错误导致这部分内存没有被释放,那么以后这部分空间将无法再被使用,产生Heap Leak。 - 系统资源泄漏
程序使用系统分配的资源,如方套接字、文件描述符、管道等没有使用对应的函数释放掉,导致系统资源的浪费,严重可导致系统效能减少,系统执行不稳定。
2.3检测内存泄漏
linux下内存泄漏检测:linux下几款内存泄漏检测工具
windows下第三方工具:VLD工具说明
其他工具:内存泄漏工具比较
2.4如何避免内存泄漏
- 工程前期良好的设计规范,养成良好的编码规范,申请的内存空间记着匹配的去释放
- 采用RAII思想或者智能指针来管理资源。
- 自己实现自带内存泄漏检测的私有内存管理库
- 出问题了使用内存泄漏工具检测
1、事前预防型。如智能指针。2、事后查错型。如泄漏检测工具。
3.智能指针
3.1文档查阅
智能指针(现代 C++)
RAII (Resource Acquisition Is Initialization)
3.2代码初识
template<class T>
class SmartPtr
{
public:
SmartPtr(T* ptr)
:_ptr(ptr)
{
}
~SmartPtr()
{
cout << "delete: " << _ptr << endl;
delete _ptr;
}
T& operator*()
{
return *_ptr;
}
T* operator->()
{
return _ptr;
}
private:
T* _ptr;
};
int div()
{
int a, b;
cin >> a >> b;
if (b == 0)
throw invalid_argument("除0错误");
return a / b;
}
void Func()
{
SmartPtr<int> sp1(new int(1));
SmartPtr<int> sp2(new int(2));
cout << div() << endl;
*sp1 = 10;
cout << *sp1 << endl;
cout << *sp2 << endl;
}
int main()
{
try
{
Func();
}
catch (exception& e)
{
cout << e.what() << endl;
}
return 0;
}
p1抛异常没问题 p2抛异常当前函数栈帧结束 p1对象析构 div函数抛异常 同前
3.3RAII思想
RAII(Resource Acquisition Is Initialization) : 一种利用对象生命周期来控制程序资源(如内
存、文件句柄、网络连接、互斥量等等)的简单技术。
解读
> 对象构造时获取资源,控制对资源的访问使之在对象的生命周期内始终保持有效,
> 对象析构时释放资源。实际上把管理资源的责任托管给了一个对象
> 一个类对象销毁时自动调用构造函数和析构函数 当函数栈帧销毁时对象自动销毁
> 实际上是因为这两个固有的"自动"成就了智能指针
优势
不需要显式释放资源
对象所需资源在其生命期内始终保持有效
3.4智能指针的设计
template<class T>
class SmartPtr
{
public:
SmartPtr(T* ptr)
:_ptr(ptr)
{
}
~SmartPtr()
{
cout << "delete: " << _ptr << endl;
delete _ptr;
}
T& operator*()
{
return *_ptr;
}
T* operator->()
{
return _ptr;
}
private:
T* _ptr;
};
int div()
{
int a, b;
cin >> a >> b;
if (b == 0)
throw invalid_argument("除0错误");
return a / b;
}
void Func()
{
SmartPtr<int> sp1(new int(1));
SmartPtr<int> sp2(new int(2));
cout << div() << endl;
*sp1 = 10;
cout << "* sp1 == " << *sp1 << endl;
cout << "* sp2 == " << *sp2 << endl;
}
3.5智能指针的拷贝问题
1.代码问题
template<class T>
class SmartPtr
{
public:
SmartPtr(T* ptr)
:_ptr(ptr)
{
}
~SmartPtr()
{
cout << "delete: " << _ptr << endl;
delete _ptr;
}
T& operator*()
{
return *_ptr;
}
T* operator->()
{
return _ptr;
}
private:
T* _ptr;
};
int main()
{
SmartPtr<int> sp1(new int(1));
SmartPtr<int> sp2(sp1);
return 0;
}
很明显这里时之前就讲过的深拷贝问题 函数结束时对同一块空间释放两次 有人说直接深拷贝不就ok了?
而这样的回答实际上时忘记了我们设计智能指针的初衷 即它要像指针一样工作 对于指针的拷贝就是两个不同的指针指向同一块空间 那怎么办?
有的人可能有这样的问题 之前的模拟实现容器时 我们自己搞了一个迭代器 为什么迭代器的浅拷贝没问题 而智能指针的而浅拷贝有问题?
迭代器是一个类 对于一个类对象 当对象销毁 会调用析构函数 默认生成的析构函数对于内置类型不做处理 对于自定义类型调用他的析构函数 自己实现的迭代器没有手写析构函数 于是调用自动生成的 而在迭代器里 指针是一个内置类型 不做处理
而在智能指针类里 我们手写了析构函数 并且在这个析构函数里还释放了指针指向的空间于是就造成了上述问题
2.初代智能指针
C++98#include <memory> auto_ptr;
template<class T>
class auto_ptr
{
public:
auto_ptr(T* ptr)
:_ptr(ptr)
{
}
~auto_ptr()
{
if (_ptr)
{
cout << "delete:" << _ptr << endl;
delete _ptr;
}
}
//构造函数: 管理权转移
auto_ptr(auto_ptr<T>& ap)
:_ptr(ap._ptr)
{
ap._ptr = nullptr;
}
T& operator*()
{
return *_ptr;
}
T* operator->()
{
return _ptr;
}
private:
T* _ptr;
};
void test_auto()
{
auto_ptr<int> ap1(new int(1));
auto_ptr<int> ap2(ap1);
//ap1失去管理权 悬空指针
*ap1 = 1;
*ap2 = 1;
}
3.智能指针的发展
Boost是啥?
3.6智能指针的升级
不需要拷贝的场景设置为禁止拷贝
C++98
C++11
template<class T>
class unique_ptr
{
public:
unique_ptr(T* ptr)
:_ptr(ptr)
{
}
~unique_ptr()
{
if (_ptr)
{
cout << "delete:" << _ptr << endl;
delete _ptr;
}
}
T& operator*()
{
return *_ptr;
}
T* operator->()
{
return _ptr;
}
// C++11防拷贝
unique_ptr(const unique_ptr<T>& up) = delete;
unique_ptr<T>& operator=(const unique_ptr<T>& up) = delete;
/*
private:
// C++98防拷贝
// 1.声明为私有 --禁止在类外实现
// 2.不实现 --编译器不自动生成浅拷贝
unique_ptr(const unique_ptr<T>& up);
unique_ptr<T>& operator=(const unique_ptr<T>& up);
*/
private:
T* _ptr;
};
void test_unique()
{
unique_ptr<int> up1(new int(1));
//unique_ptr<int> up2(up1);
}