Skip to main content
 首页 » 编程设计

C++中的智能指针、轻量级指针、强弱指针学习笔记

2022年07月19日156unruledboy

一、智能指针学习总结

1.一个非const引用无法指向一个临时变量,但是const引用是可以的!

2.C++中的delete和C中的free()类似,delete NULL不会报"double free"的oops。

int main(int argc, char **argv) 
{     
    int i; 
    int *p = new int; 
    delete p; 
    p = NULL; 
    delete p; 
    return 0; 
}

3.智能指针的实现思想:使用可以自动销毁的局部对象来描述不可以自动销毁的位于堆空间中的对象指针,以达到不需要考虑释放内存,避免内存泄漏的目的。

4.使用智能指针的限制:想使用智能指针处理一个对象,这个对象必须要继承RefBase类, 以便智能指针能操作类提供引用计数操作!

5.智能指针示例代码

#include <iostream> 
using namespace std; 
 
class RefBase { 
private: 
    int refcount; 
public: 
    RefBase() : refcount(0) {} 
 
    void incStrong(void) { 
        refcount++; 
    } 
    void decStrong(void) { 
        refcount--; 
    } 
    int getStrong(void) { 
        return refcount; 
    }     
}; 
 
class Person : public RefBase { 
public: 
    Person() { 
        cout << "Person()" << endl; 
    } 
    ~Person() { 
        cout << "~Person()" << endl; 
    } 
    void printInfo() { 
        cout << "printInfo()" << endl; 
    } 
}; 
 
template<typename T> 
class sp { 
private: 
    T *p; 
 
public: 
    sp() : p(NULL) { 
        cout << "sp()" << endl; 
    } 
 
    sp(T *p) { 
        cout << "sp(T *p)" << endl; 
        this->p = p; 
        this->p->incStrong(); 
    } 
 
    sp(const sp & other) { 
        cout << "sp(const sp & other)" << endl; 
        this->p = other.p; 
        this->p->incStrong(); 
    } 
 
    ~sp() { 
        cout << "~sp()" << endl; 
        this->p->decStrong(); 
        if (!this->p->getStrong()) { 
            delete p; //atomic call ~Person() 
            cout << "delete p" << endl; 
        } 
    } 
 
    T* operator->() { 
        return this->p; 
    } 
    T& operator*() { 
        return *(this->p); 
    } 
}; 
 
/*-----------------usage------------------*/ 
void test_func(sp<Person>& p) { 
    sp<Person> p1 = p; 
 
    p1->printInfo(); 
 
    (*p1).printInfo(); 
 
    sp<Person> p2 = new Person(); 
 
    sp<Person> p3 = new Person(); 
} 
 
int main()  
{ 
    sp<Person> p1 = new Person(); 
 
    for (int i = 0; i < 2; i++) { 
        cout << "-------" << i << "-------" << endl; 
        test_func(p1); 
        cout << "-------" << "-" << "-------" << endl; 
    } 
 
    cout << "-------end main()---------" << endl; 
}

二、轻量级指针学习笔记

1.上面智能指针存在的问题:引用计数的加减不是原子的,也就是非线程安全的!

2.Android自带轻量级智能指针:
frameworks/rs/cpp/util/RefBase.h class LightRefBase
frameworks/rs/cpp/util/StrongPointer.h class sp


3.Android提供的一套函数来保证mCount的原子操作(摘抄自frameworks/rs/cpp/util/RefBase.h)
__sync_fetch_and_sub(&mCount, 1);
__sync_fetch_and_add(&mCount, 1);


4.LightRefBase不是对线程安全的,只是对mCount是线程安全的

class LightRefBase 
{ 
public: 
    inline void decStrong(__attribute__((unused)) const void* id) const { 
        if (__sync_fetch_and_sub(&mCount, 1) == 1) { 
            /* 
             * 1.若此时被切走,且执行了sp<Person> s3 = s; 那么s的引用计数就为1了,此时就不应该 
             * 再删除了。所以轻量级指针只是对mCount是线程安全的!!! 对于对象本身并不是线程安全的, 
             * 在多线程中使用智能指针的时候必须由你自己来保证这些智能指针的使用是线程安全的。 
             *  
             * 2.将this指针强制类型转换为当前对象T, 所有的对象实现前提都应该先继承LightRefBase对象。 
            */ 
            delete static_cast<const T*>(this); 
        } 
    } 
}

5.Android实现的轻量级指针,StrongPointer.h中delete p;放在了decStrong()中了。

6.一个类中不能定义本类的成员对象!
class Person {
  Person father; //“error: field ‘father’ has incomplete type”
};

7.轻量级指针示例代码

#include <iostream> 
#include "RefBase.h" /*来自:frameworks/rs/cpp/util/*/ 
 
using namespace std; 
using namespace android::RSC; 
 
class Person : public LightRefBase<Person> { 
public: 
    Person() { 
        cout << "Person()" << endl; 
    } 
    ~Person() { 
        cout << "~Person()" << endl; 
    } 
    void printInfo() { 
        cout << "printInfo()" << endl; 
    } 
}; 
 
/*-----------------usage------------------*/ 
void test_func(sp<Person>& p) { 
    sp<Person> p1 = p; 
    p1->printInfo(); 
    (*p1).printInfo(); 
    sp<Person> p2 = new Person(); 
    sp<Person> p3 = new Person(); 
} 
 
int main()  
{ 
    sp<Person> p1 = new Person(); 
 
    for (int i = 0; i < 2; i++) { 
        cout << "-------" << i << "-------" << endl; 
        test_func(p1); 
        cout << "-------" << "-" << "-------" << endl; 
    } 
    cout << "-------end main()---------" << endl; 
}

三、Android弱指针的引入

1.上面实现的代码的缺点是没法处理某些复杂的情况:例如有两个智能指针,A引用到B, B也引用到A,最后将没办法释放A和B。因为想释放A时它被B引用着,没法释放,当想释放B时它被A引用着,也没法释放。 

2.例子代码

void test_fun() { 
    sp<Person> a = new Person(); //count=1 
    sp<Person> b = new Person(); //count=1 
    a->setFather(b); //b's count=2 
    b->setFather(a); //a's count=2 
} 
test_fun()执行完后: 
~a(); //a's count=1 
~b(); //b's count=1 
count不为0,无法释放。
#include <iostream> 
#include "RefBase.h" 
 
using namespace std; 
using namespace android::RSC; 
 
/* LightRefBase is a template */ 
class Person : public LightRefBase<Person> { 
private: 
    sp<Person> father; 
    sp<Person> son; 
public: 
    Person() { 
        cout << "Person()" << endl; 
    } 
    ~Person() { 
        cout << "~Person()" << endl; 
    } 
 
    void setFather(sp<Person> &p) { 
        this->father = p; 
    } 
    void setSon(sp<Person> &p) { 
        this->son = p; 
    } 
}; 
 
 
/*-----------------usage------------------*/ 
void test_func() { 
    /* 1. 对于 new Person() 
     * 1.1 Person对象里的father先被构造 
     * 1.2 Person对象里的son被构造 
     * 1.3 Person对象本身 
     * 2. Person对象的指针传给"sp<Person> father" 
     *    导致: sp(T* other) 被调用, 它增加了这个Person对象的引用计数(此时mCount=1) 
     */ 
    sp<Person> father = new Person(); 
 
    /* 1. 对于 new Person() 
     * 1.1 Person对象里的father先被构造 
     * 1.2 Person对象里的son被构造 
     * 1.3 Person对象本身 
     * 2. Person对象的指针传给"sp<Person> son" 
     *    导致: sp(T* other) 被调用, 它增加了这个Person对象的引用计数(此时mCount=1) 
     */ 
    sp<Person> son = new Person(); 
 
    /* setSon()内部实现是一个"=" : this->son = son 
     * "="在StrongPointer.h中被重载, 它会再次增加该Person对象的引用计数 
     * 所以son对象对应的Person对象的引用计数增加为2 
     */ 
    father->setSon(son); 
     
    /* setFather()内部实现是一个"=" : this->father = father 
     * "="在StrongPointer.h中被重载, 它会再次增加该Person对象的引用计数 
     * 所以father对象对应的Person对象的引用计数增加为2 
     */ 
    son->setFather(father); 
 
    /* 当test_func执行完时, father和son被析构 
     * 1.先看father: 
     *    ~sp(): decStrong, 里面会将计数值减1, father对应的Person的计数值等于1, 还没等于0, 所以没有delete 
     * 2.对于son: 
     *    ~sp(): decStrong, 里面会将计数值减1, son对应的Person的计数值等于1, 还没等于0, 所以没有delete 
     */ 
} 
 
int main()  
{ 
    test_func(); /*导致内存泄漏*/ 
}

3.分析

4.什么是强弱指针
强指针/强引用:A指向B, A可以决定B的生死(就是会触发incStrong()/decStrong()被调用)
弱指针/弱引用:A指向B, 但是A不可以决定B的生死(弱指针的引用不会修改mCount引用计数,因此也不能通过弱指针引用对象成员)

5.对于交叉引用导致双方都没办法释放的问题,只有引入弱指针来解决。

四、Android强弱指针的实现与使用

1.Android5.0中强弱指针实现位置
文件:
system/core/include/utils
system/core/include/cutils
cpp:
system/core/libutils/RefBase.cpp

Android 8.0变成下面路径,但是使用它build不过(应该加上"g++ -std=c++11" 就可以编译过了),于是还是使用上面Android5.0的。
../libcutils/include/cutils/
../libutils/include/utils/

编译方法:
拷贝android-5.0.2/system/core/include目录到当前目录,使用它里面的头文件
拷贝system/core/libutils/RefBase.cpp到当前目录使用它里面的weakref_impl
解决编译问题:./include/log/uio.h中和/usr/include/x86_64-linux-gnu/bits/uio.h都定义了struct iovec,注释掉本地的定义即可。
解决编译问题:RefBase.cpp:(.text+0x4a): undefined reference to `android_atomic_inc',在当前目录下grep 此函数,发现其在某些头文件中已经定义了
于是在RefBase.cpp中增加#include <cutils/atomic-x86_64.h>
解决编译问题:RefBase.cpp:(.text+0xa4): undefined reference to `__android_log_assert',对应的解决方法是注释掉RefBase.cpp中的所有ALOG_ASSERT。或者
直接在RefBase.cpp:中#undef ALOG_ASSERT #define ALOG_ASSERT(...) 

然后编译成功!


1.强弱指针实现对比
之前:
①.Person继承于LightRefBase
②.mCount提供引用计数
③.加减引用计数:incStrong()/decStrong()

现在:
①.Person继承于RefBase
②.mStrong和mWeak提供引用计数,分别为强/弱引用提供引用计数。其中mStrong就是和之前mCount作用一致。
③.加减引用计数:incStrong()/decStrong()和incWeak()/decWeak()

3.强弱指针的使用
sp<Person> p1 = new Person(); //使用强指针
wp<Person> p2 = new Person(); //使用弱指针

4.system/core/libutils/RefBase.cpp 中实现了 frameworks/rs/cpp/util/RefBase.h 中使用的类weakref_impl

5.无法通过弱指针引用对象的成员

//eg: 
int main() 
{ 
    wp<Person> s = new Person(); 
    s->printInfo(); //编译不过,因为弱指针并没有重载“->” 
    return 0; 
}

wp只是简单的引用Person而已,并没有控制Person的生死,使用的时候有可能Person已经被销毁了,所以s->printInfo();的使用是不对的,所以直接就没有重载"->",使你用不了(也没有重载解引用的"*")。需要把弱指针升级为强指针后再去访问类的成员!

调用弱指针的promote()将其升级为强指针后再访问对象的成员:

int main() 
{ 
    wp<Person> s1 = new Person(); 
    sp<Person> s2 = s1.promote(); /*将弱指针升级为强指针*/ 
    //if (s2) { /*报错没有办法将sp<Person>对象转化为bool变量*/ 
    if (s2 != 0) /*但是这是OK的!*/ 
        s2->printInfo(); 
    } 
    return 0; 
}

6.使用弱指针消除交叉引用导致无法释放的问题
将Person内部的father和son成员改为弱指针定义,使用弱指针就不会出现由于双方相互引用导致死锁都不能被释放的问题。
wp<Person> father;
wp<son> son;

7.class weakref_impl(system/core/libutils/RefBase.cpp)中的mFlags有两个取值:
/*weakref_impl中的mFlags的取值范围如下*/
enum {
  OBJECT_LIFETIME_STRONG = 0x0000, //表示对象的生命周期由mStrong决定,默认取值是这个,一般取这个值
  OBJECT_LIFETIME_WEAK = 0x0001, //表示对象的生命周期由mWeak决定
  OBJECT_LIFETIME_MASK = 0x0001
};

8.system/core/libutils/RefBase.cpp中的incStrong()中同样增加了弱指针的引用计数refs->incWeak(id);
WDS:mStrong计数是永远小于或等于mWeak计数的

9.测试Demo

#include <iostream> 
#include <string.h> 
#include <unistd.h> 
#include <utils/RefBase.h> 
 
using namespace std; 
using namespace android; 
 
 
class Person : public RefBase { 
 
private: 
    const char *name; 
    /* memory leack */ 
    //sp<Person> father; 
    //sp<Person> son; 
 
    /* no memory leack */ 
    wp<Person> father; 
    wp<Person> son; 
 
public: 
    Person() { 
        cout <<"Pserson()"<<endl; 
    } 
 
    Person(const char *name) { 
        cout <<"Pserson()"<<endl; 
        this->name = name; 
    } 
 
    ~Person() 
    { 
        cout << "~Person()"<<endl; 
    } 
 
    void setFather(sp<Person> &father) 
    { 
        this->father = father; 
    } 
 
    void setSon(sp<Person> &son) 
    { 
        this->son = son; 
    } 
     
    void printInfo(void) 
    { 
        cout<<"just a test function"<<endl; 
    } 
    const char* getName() { 
        return this->name; 
    } 
    friend void helper_fun(sp<Person> &p); 
}; 
 
void test_func1() 
{ 
    sp<Person> father = new Person(); 
    sp<Person> son = new Person(); 
 
    father->setSon(son); 
    son->setFather(father); 
} 
 
void test_func2() 
{ 
    wp<Person> p1 = new Person(); 
    //p1->printInfo(); //error, wp not reload operator-> 
    sp<Person> p2 = p1.promote(); //promote() povide by wp 
    if (p2 != 0) { 
        p2->printInfo(); 
    } 
} 
 
void helper_fun(sp<Person> &p) { 
    sp<Person> p1; 
    cout << "I am " << p->name << ", "; 
    p1 = p->father.promote(); 
    if (p1 != 0) { 
        cout << "my father is " << p1->getName() << endl; 
    } 
    p1 = p->son.promote(); 
    if (p1 != 0) { 
        cout << "my son is " << p1->getName() << endl; 
    } 
} 
 
void test_func3() 
{ 
    sp<Person> father = new Person("XiaoTouBaBa"); 
    sp<Person> son = new Person("DaTouErZi"); 
 
    father->setSon(son); 
    son->setFather(father); 
 
    helper_fun(father); 
    helper_fun(son); 
} 
 
 
int main(int argc, char *argv[]) 
{ 
    int choose; 
 
    if (argc != 2) { 
        cout << "argc != 2" << endl; 
        return -1; 
    } 
    choose = *argv[1]; 
 
    if (choose == '1') { 
        test_func1(); 
    } else if (choose == '2') { 
        test_func2(); 
    } else if (choose == '3') { 
        test_func3(); 
    } 
 
    return 0; 
}

补充:

1.在RefBase 里面有两个变量mStrong, mWeak 分别保存强弱引用计数,只要强引用计数为0,强制delete。

2.通过类图可以发现,强指针实现了 "." "->" 操作符的重载,sp 可以直接访问类成员,而 wp 不能,因为它没有重载这两个操作符。
但是可以将 wp 转化为 sp 后访问类成员。

3.在C++11中,引入了智能指针。主要有:unique_ptr, shared_ptr, weak_ptr。

4.不是wp/sp类需要继承RefBase,而是类对象T必须继承的

弱指针仅仅记录该对象的地址,不能通过弱指针来访问该对象,也就是说不能通过弱指针来调用对象的成员函数或访问对象的成员变量。
要想访问弱指针所指向的对象,需首先将弱指针升级为强指针(通过wp类所提供的promote()方法)。


优秀博文:
Android 强弱指针分析:https://blog.csdn.net/chituhuan/article/details/53439266


本文参考链接:https://www.cnblogs.com/hellokitty2/p/10646353.html