TOPIC NOTES

C/C++基础之三(指针引起的内存泄露)

C++内存管理
文章目录
  1. 注意观察下面的代码,如果new出来的对象忘记被delete释放,是会发生内存泄漏的。
  2. 那么如何让使用指针不必有那么大的心智负担了?因为局部变量使用之后,程序会自动帮我调用其析构函数,因此我们要将指针变量的创建修改修改:
  3. 输出如下,可以看到我们并没有主动delete,程序就避免了内存泄漏。
  4. 接下来我们继续改进:
  5. 输出如下,程序发生了崩溃,因为两次进行delete p;
  6. 没事,遇事不要慌,东西都在后备箱,看我们的破解大法
  7. Person只new了一个,因此也只析构了一次,程序正确:
  8. 程序似乎还不够优美,1. 引用计数写在Person里面的 2. 如果换一个类怎么办了?
  9. 输出
  10. 那么到这里是否就结束了呢?No, 并没有,实际上这个代码是线程不安全的。Android源码中就有很好的例子,__sync_fetch_and_add就是确保线程安全。

注意观察下面的代码,如果new出来的对象忘记被delete释放,是会发生内存泄漏的。
cpp
#include <iostream>

using namespace std;

class Person{
private:
    int age;
public:
    // 构造函数
    Person(){
        cout << "Person()" << endl;
    }
    void print_info(){
        cout << "person_info" << endl;
    }
    //析构函数
    ~Person(){
        cout << "~Person()" << endl;
    }
};

void test_func(){
    Person *p = new Person();
    p->print_info();
    // delete p;
}

int main(){
    test_func();
}

那么如何让使用指针不必有那么大的心智负担了?因为局部变量使用之后,程序会自动帮我调用其析构函数,因此我们要将指针变量的创建修改修改:
cpp
//引入sp,其意为SmartPoint
class sp{
private:
    Person *p;
public:
    sp(){
        p = NULL;
        cout << "sp()" << endl;
    }

    sp(Person *other){
        this->p = other;
        cout << "sp(Person *other)" << endl;
    }

    ~sp(){
        if (p)
        {
           delete p;
        }
        cout << "~sp()" << endl;
    }

    // 对 -> 操作符进行重载
    Person* operator->(){
        return p;
    }

};


void test_func(){
    sp s = new Person();
    s->print_info();
}

输出如下,可以看到我们并没有主动delete,程序就避免了内存泄漏。
text
Person()
sp(Person *other)
person_info
~Person()
~sp()

接下来我们继续改进:
text
    sp(Person *other){
        this->p = other;
        cout << "sp(Person *other)" << endl;
    }
    // const是不能少的,
    sp(const sp &other){
        this->p = other.p;
        cout << " sp(sp *other)" << endl;
    }

    ~sp(){
        if (p)
        {
           delete p;
        }
        cout << "~sp()" << endl;
    }

    // 对 -> 操作符进行重载
    Person* operator->(){
        return p;
    }

};


void test_func(sp& s1){
    // 会调用拷贝构造函数:sp(const sp &other)
    sp s = s1; // 这是初始化,而不是赋值 ,赋值是:sp s; s = s1;  赋值会调用operator= 操作符函数,初始化会调用拷贝构造函数
    s->print_info();
}

int main(){

    sp s = new Person();
    for(int i=0;i<2;i++){
        test_func(s);
    }

}

输出如下,程序发生了崩溃,因为两次进行delete p;
text
Person()
sp(Person *other)
 sp(const sp *other)
person_info
~Person()
~sp()
 sp(const sp *other)
person_info
~Person()

没事,遇事不要慌,东西都在后备箱,看我们的破解大法
text
class Person{
private:
    int age;
    int count;
public:
    // 构造函数
    Person(){
        cout << "Person()" << endl;
    }
    //增加引用计数
    void incCount(){
        this->count++;
    }
    //减少引用计数
    void decCount(){
        this->count--;
    }
    // 获取引用计数
    int getCount(){
        return this->count;
    }
    void print_info(){
        cout << "person_info" << endl;
    }
    //析构函数
    ~Person(){
        cout << "~Person()" << endl;
    }
};

//引入sp,其意为SmartPoint
class sp{
private:
    Person *p;
public:
    sp(){
        p = NULL;
        cout << "sp()" << endl;
    }

    sp(Person *other){
        this->p = other;
        this->p->incCount();
        cout << "sp(Person *other)" << endl;
    }
    // const是不能少的,
    sp(const sp &other){
        this->p = other.p;
        this->p->incCount();
        cout << " sp(const sp *other)" << endl;
    }

    ~sp(){
        if (p)
        {
            if (p->getCount()>1)
            {   //存在多个引用
                p->decCount();
            }else{
                delete p;
            }
        }
        cout << "~sp()" << endl;
    }

    // 对 -> 操作符进行重载
    Person* operator->(){
        return p;
    }

};

Person只new了一个,因此也只析构了一次,程序正确:

text
Person()
sp(Person *other)
 sp(const sp *other)
person_info
~sp()
 sp(const sp *other)
person_info
~sp()
~Person()
~sp()

程序似乎还不够优美,1. 引用计数写在Person里面的 2. 如果换一个类怎么办了?
  1. 引入RefBase基类用于计数

  2. 使用模板类实现任意对象的自定义只能指针

一切尽在不言中,上代码

cpp
class RefBase{
private:
    int count; //构造对象时不初始化会得到一个无法想象的结果
public:

    RefBase():count(0){
        cout << "RefBase()" << endl;
    }

    void incCount(){
        this->count++;
    }
    void decCount(){
        this->count--;
    }
    int getCount(){
        return this->count;
    }

    virtual ~RefBase(){
        cout << "~RefBase" << endl;
    }

};


class Person : public RefBase{
private:
    int age;

public:
    // 构造函数
    Person(){
        cout << "Person()" << endl;
    }
    void print_info(){
        cout << "person_info" << endl;
    }
    //析构函数
    ~Person(){
        cout << "~Person()" << endl;
    }
};

//引入sp,其意为SmartPoint
template<typename T>
class sp{
private:
    T *p;
public:
    sp(){
        p = NULL;
        cout << "sp()" << endl;
    }

    sp(T *other){
        this->p = other;
        this->p->incCount();
        cout << "sp(Person *other)" << endl;
    }
    // const是不能少的, 拷贝构造函数:sp s2 = s1;  sp s3(s1); 这两种创建对象的方式都会调用拷贝构造函数;  sp s4; s4 = s1; 这种写法只会调用 operator= 操作符函数
    sp(const sp &other){
        this->p = other.p; // 指针的直接拷贝
        this->p->incCount();
        cout << " sp(const sp *other)" << endl;
    }

    ~sp(){
        if (p)
        {
            cout << "value is :" << p->getCount() << endl;
            if (p->getCount()>1)
            {   //存在多个引用
                p->decCount();
            }else{
                delete p;
            }
        }
        cout << "~sp()" << endl;
    }

    // 对 -> 操作符进行重载,返回Person的指针
    T* operator->(){
        return p;
    }

};

template<typename T>
void test_func(sp<T>& s1){
    // 会调用拷贝构造函数:sp(const sp &other)
    sp<T> s = s1; // 这是初始化,而不是赋值 ,赋值是:sp s; s = s1;  赋值会调用operator= 操作符函数,初始化会调用拷贝构造函数
    s->print_info();
}

int main(){
    //只new了一次,
    sp<Person> s = new Person();
    //test_func结束被释放两次???
    for(int i=0;i<2;i++){
        test_func(s);
    }

}

输出
text
RefBase
Person()
sp(Person *other)
 sp(const sp *other)
person_info
value is :2
~sp()
 sp(const sp *other)
person_info
value is :2
~sp()
value is :1
~Person()
~RefBase
~sp()

那么到这里是否就结束了呢?No, 并没有,实际上这个代码是线程不安全的。Android源码中就有很好的例子,__sync_fetch_and_add就是确保线程安全。

cpp
inline LightRefBase() : mCount(0) { }
inline void incStrong(__attribute__((unused)) const void* id) const {
    __sync_fetch_and_add(&mCount, 1);
}
inline void decStrong(__attribute__((unused)) const void* id) const {
    if (__sync_fetch_and_sub(&mCount, 1) == 1) {
        delete static_cast<const T*>(this);
    }
}

每一个不曾起舞的日子,都是对生命的一种辜负。

继续阅读C++