C++基础篇:09 智能指针与异常处理

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,使用会有警告

        头文件:

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 p1(new int);

        unique_ptr p2(p1);    //   报错

        p2 = p1;    //  报错

        p2 = unique_ptr(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 抛出。 需要导入

    std::invalid_argument 当使用了无效的参数时,会抛出该异常

    std::length_error 当创建了太长的string时,会抛出该异常

8 自定义异常类

    见例子 10error.cpp

9 使用异常需要注意的问题

① 不要抛出局部变量的地址、引用

② 如果使用引用类型来捕获异常,就会少一次拷贝

③ 不要在构造函数、析构函数中抛异常

④ C++中异常经常会导致资源泄漏问题,比如在new\detele过程中抛出异常并捕获而没有处理,可能导致内存泄漏,可以借助智能指针避免,在lock和unlock之间抛出异常,导致锁没法解开,别的线程可能在等待该锁的解锁,导致死锁问题

你可能感兴趣的:(C++,c++,数学建模,开发语言)