C++中的智能指針、輕量級指針、強弱指針學習筆記


一、智能指針學習總結

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

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM