JHHK

欢迎来到我的个人网站
行者常至 为者常成

17、异常、智能指针

目录

异常

一、异常

编程过程中的常见错误类型
语法错误
逻辑错误
异常
……

异常是一种在程序运行过程中的发生的不好预测的错误(比如内存不够)
异常没有被处理,会导致程序终止

为了增强可读性和方便团队协作,如果函数内部可能会抛出异常,建议函数声明一下异常类型

//int divide(int v1,int v2) throw(){//不抛出任何异常
//int divide(int v1,int v2) throw(const char*,int){//只抛出const char*,int类型的异常
int divide(int v1,int v2){//抛出任意可能的异常
    if (v2 == 0) {
        throw "不能除以0";
//        throw 0;
    }
    
    return v1/v2;
    
    
    /**
     throw异常后,会在当前函数中查找匹配的catch,找不到就
     终止当前函数代码,去上一层函数中查找。如果最终都找不
     到匹配的catch,整个程序就会终止
     但一般不会在抛出异常的函数里再捕获异常
     */
//    try {
//        if (v2 == 0) { throw "不能除以0";}
//        return v1/v2;
//    } catch (const char* exception) {
//        cout<<"exception0 = "<<exception<<endl;
//    }
//    return 0;
}


void divideTest(){
    try {
        //被检测的代码
        int a = 10;
        int b = 0;
        int c = divide(a, b);
        
        cout<<"c = "<<c<<endl;
    } catch (const char* exception) {
        //异常处理代码
        cout<<"exception = "<<exception<<endl;
    } catch(int exceptionCode){
        //异常处理代码
        cout<<"exceptionCode = "<<exceptionCode<<endl;
    }
}


void memoryTest() {
    cout << "1" << endl;

    // 如果内存不够,就会抛出异常(运行过程中抛出一个错误)
    try {
        for (int i = 0; i < 9999999; i++) {
            int *p = new int[9999999];
        }

        cout << "2" << endl;
    }
    catch (...) {
        //拦截所有异常类型
        cout << "发生了异常" << endl;
    }

    cout << "3" << endl;

    // delete[] p;
}

二、自定义异常类型

class Exception{
public:
    virtual string what() const= 0;
};

class DivideException:public Exception{
public:
    string what() const{
        return "不能除以0";
    };
};


int divideFun(int v1,int v2) throw(Exception){
    if (v2 == 0) {
        throw DivideException();
    }
    
    return v1/v2;
}


void customExceptionTest(){
    try {
        int a = 10;
        int b = 0;
        int c = divideFun(a, b);
    } catch (const Exception & exception) {
        cout<<"exception.what()"<<exception.what()<<endl;
    }
    
    cout<<"running..."<<endl;
}

三、标准异常类型

void biaoZhunExceptionTest(){
    
    try {
        int size = 9999999;
        for (int i=0; i<size; i++) {
            int* p = new int[size];
        }
    } catch (const std::bad_alloc& exception) {
        cout<<"exception.what() = "<<exception.what()<<endl;
    }
    
    cout<<"running..."<<endl;
}

img

智能指针

一、定义两个类

class Car;
class Person{
public:
    shared_ptr<Car> m_car = nullptr;
    Person(){
        cout<<"Person::Person()"<<endl;
    }
    
    ~Person(){
        cout<<"Person::~Person()"<<endl;
    }
    
    void run(){
        cout<<"Person::run()"<<endl;
    }
};

class Car{
public:
//    shared_ptr<Person> m_person = nullptr;
    weak_ptr<Person> m_person;

    Car(){
        cout<<"Car::Car()"<<endl;
    }
    
    ~Car(){
        cout<<"Car::~Car()"<<endl;
    }
};

二、智能指针使用

传统指针存在的问题
需要手动管理内存
容易发生内存泄露(忘记释放、出现异常等)
释放之后产生野指针(野指针:指向了已经释放了的内存;)

智能指针就是为了解决传统指针存在的问题
auto_ptr:属于C++98标准,在C++11中已经不推荐使用(有缺陷,比如不能用于数组)
shared_ptr:属于C++11标准
unique_ptr:属于C++11标准

void smartPointTest(){
    cout<<"1"<<endl;
    
    {
        Person * p = new Person();//不会自动释放
        shared_ptr<Person> p1(new Person());//智能指针会自动释放
        shared_ptr<Person> p2 =  shared_ptr<Person>(new Person());//智能指针会自动释放
        p1->run();
        p2->run();
    }
    
    cout<<"2"<<endl;
}

三、智能指针的简单自实现

template<class T>
class SmartPointer{
    T* m_pointer;
public:
    SmartPointer(T* pointer):m_pointer(pointer){}
    
    ~SmartPointer(){
        if (m_pointer == nullptr) return;
        delete m_pointer;
    }
    
    T* operator->(){
        return m_pointer;
    }
};


void smartPointerTest2(){
    
    cout<<"1"<<endl;
    {
        
        SmartPointer<Person> p1(new Person());
        p1->run();
        //p1.operator->()->run();
        
        SmartPointer<Person> p2 = SmartPointer<Person>(new Person());
        p2->run();
        
    }
    
    cout<<"2"<<endl;
}

四、智能指针设计原理

多个shared_ptr可以指向同一个对象,当最后一个shared_ptr在作用域范围内结束时,对象才会被自动释放

一个shared_ptr会对一个对象产生强引用(strong reference)
每个对象都有个与之对应的强引用计数,记录着当前对象被多少个shared_ptr强引用着

可以通过shared_ptr的use_count函数获得强引用计数
当有一个新的shared_ptr指向对象时,对象的强引用计数就会+1
当有一个shared_ptr销毁时(比如作用域结束),对象的强引用计数就会-1
当一个对象的强引用计数为0时(没有任何shared_ptr指向对象时),对象就会自动销毁(析构)

void sharedPtrTheory(){
    shared_ptr<Person> ptr1(new Person());
    cout<<ptr1.use_count()<<endl;//1
    {
        shared_ptr<Person> ptr2 = ptr1;
         cout<<ptr1.use_count()<<endl;///2
    }
    cout<<ptr1.use_count()<<endl;//1
}
void sharedPtrNote(){
    
    //shared_ptr的注意点
     /**
      不要使用裸指针来初始化智能指针,比如以下代码
      */
    Person * p = new Person();
    cout<<"p = "<<p<<endl;
    {
        shared_ptr<Person> ptr1(p);
        cout<<"ptr1 = "<<ptr1<<endl;
        cout<<ptr1.use_count()<<endl;
    }
    //离开作用域person对象释放,此时p指针应该是野指针

    {
        shared_ptr<Person> ptr2(p);
        cout<<"ptr2 = "<<ptr2<<endl;
        cout<<ptr2.use_count()<<endl;
    }
    //离开作用域重复释放person对象,
    //报错 malloc: *** error for object 0x102834560: pointer being freed was not allocated

    cout<<"-----------------"<<endl;
    
    // 可以通过一个已存在的智能指针初始化一个新的智能指针
    shared_ptr<Person> ptr3(new Person());
    shared_ptr<Person> ptr4(ptr3);
    shared_ptr<Person> ptr5 = ptr3;
    cout<<ptr3.use_count()<<endl;
}

unique_ptr

unique_ptr也会对一个对象产生强引用,它可以确保同一时间只有1个指针指向对象
当unique_ptr销毁时(作用域结束时),其指向的对象也就自动销毁了
可以使用std::move函数转移unique_ptr的所有权

void uniquePtrTest(){
    //ptr强引用这Person对象
    unique_ptr<Person> ptr(new Person());
    cout<<"1"<<endl;
    cout<<"ptr = "<<ptr<<endl;//ptr = 0x100695880
    {
        //转移之后ptr2强引用这Person对象
        unique_ptr<Person> ptr2 = std::move(ptr);
        cout<<"ptr2 = "<<ptr2<<endl;//ptr = 0x100695880
        cout<<"ptr = "<<ptr<<endl;//ptr = 0x0
        cout<<"2"<<endl;
    } 
    cout<<"3"<<endl;
}

智能指针与数组

针对数组的用法
shared_ptr的数组智能指针,有和->操作,但不支持下标操作[],只能通过get()去访问数组的元素。
shared_ptr的数组智能指针,必须要自定义deleter
unique_ptr的数组智能指针,没有
和->操作,但支持下标操作[]
参考文章:https://www.cnblogs.com/xiaoshiwang/p/9726511.html

void sharedPtrForArray(){
    shared_ptr<Person> ptr1(new Person[5]{},[](Person*p){delete [] p;});
    
    ptr1.get()->run();//数组的第一个元素
    (ptr1.get()+1)->run();//数组的第二个元素
//    shared_ptr<Person[]> ptr2(new Person[5]{});//不支持
    
    unique_ptr<Person[]> persons(new Person[5]{});
    persons[0].run();
}

shared_ptr的循环引用

void cycleReferenceTest(){
    
    shared_ptr<Person> ptr_person(new Person());
    shared_ptr<Car> ptr_car(new Car());
    ptr_person->m_car = ptr_car;
    ptr_car->m_person = ptr_person;
    
    /**
     离开当前函数的作用域后person对象和car对象都不会销毁,产生了强引用
     见图:cycleReference.png。离开作用域只是画X号的两个强引用消失了,剩下的实线形成循环引用
     
     如果将Car内的成员变量m_person或Person内的成员变量m_car变为弱引用即可解决循环引用的问题
     见图:weakPtr.png
     //shared_ptr<Person> m_person = nullptr;
     weak_ptr<Person> m_person;
     */
}

img

img


行者常至,为者常成!





R
Valine - A simple comment system based on Leancloud.