1 智能指针
1.1 常规指针的缺点
当一个常规指针离开作用域时,只有该指针变量所占用的内存空间(4/8字节)会释放,而它所指向的内存空间不会自动释放,当free、delete、delete[]语句忘记写、无法执行,就造成内存泄漏
1.2 智能指针的优点
① 智能指针是一个封装了常规指针的类类型,并在该类中重载了 * -> []运算符
② 当智能指针对象离开作用域,它的析构函数会自动地释放常规指针所指向的内存,从而达到自动释放的效果,目的是为了避免内存泄漏
1.3 智能指针与常规指针的相同点
它们的操作就跟操作普通指针一样
2 C++STL中的智能指针
STL中提供了auto_ptr\unique_ptr\shared_ptr\weak_ptr种智能指针,在C++98语法标准中只有auto_ptr,后面三个是C++11语法标准中增加的,并且弃用了auto_ptr,使用会有警告
头文件:<memory>
2.1 auto_ptr
采用独占式拥有模式,不能同时有多个auto_ptr指向同一个对象,但是不能完全实现,所以有隐患,所以C++11中产生使用警告
auto_ptr<类型名> p(new 类型名);
类型名* p = new 类型名;
auto_ptr<类型名> atuo_p(p);
auto_ptr<类型名> p2;
p2 = p1; // 不报错,p2也指向p1内存,p1可能还指向、也可能不指向,不确定
2.2 unique_ptr 独享指针
相当于auto_ptr的升级,完全实现独占式拥有模式,保证同一时间内只有一个unique_ptr指向某个对象
通过把拷贝构造、赋值操作函数声明为delete来禁用uniqued的赋值
unique_ptr<int> p1(new int);
unique_ptr<int> p2(p1); // 报错
p2 = p1; // 报错
p2 = unique_ptr<string>(new string("hehe"));//允许,允许匿名赋值,此时p2是唯一指向者
但是允许unique_ptr的匿名对象给一个unique_ptr赋值,并且可以通过C++标准库全局函数move() 转移某个unique_ptr的指向给另一个unique_ptr,而原来的unique_ptr会指向NULL,还是确保了独占
p1 = move(p2);
并且p1原来指向的内存,会在move执行转移之前先释放,确保不会内存泄漏,然后才更改指向p2原来指向的内存,最后p2指向NULL
2.3 shared_ptr 共享指针
采用共享式拥有模式,允许同一时间多个shared_ptr指向相同内存
当一个内存资源被shared_ptr指向时,内部的一个引用计数器+1
当指向该资源的某个shared_ptr离开作用域时,引用计数器-1,并放弃对该资源的引用
当某个shared_ptr调用reset()成员函数时,引用计数器-1,并放弃对该资源的引用
当该资源的引用计数减为0时,最后放弃引用的shared_ptr才会在析构函数释放该资源,以此保证不会被重复释放
2.3.1 相关shared_ptr成员函数
get() 获取指向内存的地址
use_count() 返回引用计数的数量
unique() 判断是否独占 (1:独占 0:共享)
reset() 放弃对内存的指向并且指向NULL,引用计数-1
2.3.2 C++全局标准库函数
swap() 交换两个对象的值
swap(p1,p2); //交换共享指针的指向
2.3.3 shared_ptr的循环引用问题
当两个类中都有能够相互指向对方类类型的共享指针成员变量,并且在类外分别指向两个共享指针指向new出来的两个类对象,并且让这个两个类对象的共享指针成员指向对方,这样就构成了循环引用,当这两个类对象离开作用域时,会因为产生死锁而无法执行各自的析构函数,导致内存泄漏
2.4 weak_ptr 弱引用指针
weak_ptr是为了配合shared_ptr而引入的一种智能指针,用它来指向被shared_ptr指向的资源,而不会影响该资源的生命周期,也就是说一个wear_ptr指向某个资源或者断开指向时,都不会改变该资源的引用计数,只有当该资源被最后一个shared_ptr断开指向后,引用计数为0才会被释放。但是weak_ptr是可以正常像智能指针一样访问资源,因此weak_ptr相当于shared_ptr的助手,解决一些shared_ptr无法解决的问题,例如解决shared_ptr的循环引用问题。weak_ptr不能单独使用。
3 C++的异常处理
当代码运行时出现错误,终止执行(意外)
C语言中执行到不正确结果时,可以通过返回值提前结束执行,并且可以把错误原因通过返回值传递给调用者
C语言中调用者只能立即接收返回值,或者立即使用显示
C++可以针对代码运行出错抛出异常,并通过捕获对应的异常,执行不同的错误处理语句,如果捕获成功可以不终止
4 如何抛异常
throw 数据;
数据可以是任意类型,不受函数返回值的限制,但是不要抛出局部变量的地址或引用,因为局部变量会自动销毁
5 如何捕获异常
try{
// 可能会产生异常的代码或函数调用
}catch(类型名 变量名){
// 错误处理
}
6 异常规范(异常说明)
返回值 函数名(参数列表)throw(类型名1,类型名2,...){}
异常规范:在某个函数声明时,增加一个异常规范,用于限制该函数可以抛出的异常的类型有哪些
如果在进行过异常规范的函数中,抛出不属于规范内类型的异常时,可以抛出,但是无法捕获不属于规范的类型异常
因此通过 返回值 函数名(参数列表)throw(){} //说明该函数不能抛任何异常
7 C++的标准异常
C++已经定义好了一些异常类,当某些特定异常发生时,编译器会自动抛出对应的异常类对象
std::exception 所有标准异常类的父类,可以使用该类对象来捕获所有的标准异常子类对象
std::bad_alloc new分配失败抛出该异常类对象或它的子类类对象
std::bad_cast 该异常可以通过dynamic_cast 抛出。 需要导入<typeinfo>
std::invalid_argument 当使用了无效的参数时,会抛出该异常
std::length_error 当创建了太长的string时,会抛出该异常
8 自定义异常类
#include <iostream>
using namespace std;
#define Err(...) ZhiZhenError(__FILE__,__func__,__LINE__,__TIME__,__VA_ARGS__)
class ZhiZhenError : public exception
{
string file;
string func;
size_t line;
string time;
string info;
public:
ZhiZhenError(const string& file,const string& func,size_t line,const string& time,const string& info)throw():file(file),func(func),line(line),time(time),info(info) {}
~ZhiZhenError(void)throw(){}
friend ostream& operator<<(ostream& os,const ZhiZhenError& err)
{
return os << "file:" << err.file << " func:" << err.func <<" line:" << err.line << " error:" << err.info << " time:" << err.time;
}
};
void func(int num)throw(ZhiZhenError)
{
if(num > 0)
{
cout << "good" << endl;
}
else
{
//throw ZhiZhenError(__FILE__,__func__,__LINE__,__TIME__,"num too small");
throw Err("too small");
}
}
int main(int argc,const char* argv[])
{
try{
func(-100);
}catch(ZhiZhenError err){
cout << err.what() << endl;
cout << err << endl;
}
}
9 使用异常需要注意的问题
① 不要抛出局部变量的地址、引用
② 如果使用引用类型来捕获异常,就会少一次拷贝
③ 不要在构造函数、析构函数中抛异常文章来源:https://www.toymoban.com/news/detail-489595.html
④ C++中异常经常会导致资源泄漏问题,比如在new\detele过程中抛出异常并捕获而没有处理,可能导致内存泄漏,可以借助智能指针避免,在lock和unlock之间抛出异常,导致锁没法解开,别的线程可能在等待该锁的解锁,导致死锁问题文章来源地址https://www.toymoban.com/news/detail-489595.html
到了这里,关于C++基础篇:09 智能指针与异常处理的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!