zoukankan      html  css  js  c++  java
  • C++之智能指针20170920

    /******************************************************************************************************************/

    一、C++智能指针_自己实现智能指针

    1.使用局部变量结合new的方式,防止new导致的内存泄漏

    class sp

    {

    private:

             Person *p;

    public:

             sp() : p(0) {}//表明sp的构造函数 继承person的无参构造函数       

             sp(Person *other)

       {

                       cout<<"sp(const Person *other)"<<endl;

                       p = other;

             }

             ~sp()

             {

                       cout<<"~sp()"<<endl;

                       if (p)

                                delete p;

             }

      Person *operator->()//重载->         

      {

                       return p;

            }

    };

    void test_func(void)

    {

             sp s = new Person();//分配的堆空间只是当作参数传递进去(传递给构造函数)了  

        s->printInfo();

    }

    分配的堆空间只是当作参数传递进去(传递给构造函数)了,s执行完后调用s的析构函数(局部变量,栈里),析构函数中又释放了内存就没有泄漏了

    以后代码中需要person指针就可以使用sp代替,不用担心内存泄漏

    /* 相当于:

    * 1. Person *p = new Person();

    * 2. sp tmp(p);  ==> sp(Person *other)

    * 3.

    * 3.1 sp other(tmp);  ==> sp(sp &other2)

    问题在于: sp &other2 = tmp; // 错误语法,因为引用无法等于临时变量                

    const sp& other2 = tmp; //ok

    * 或

    * 3.2 sp other(tmp ==> Person*);  ==>sp(Person *other)

    */

    sp other = new Person();//编译器优化,直接把2,3部合并为一步

    for (i = 0; i < 2; i++)

                       test_func(other);//这里执行两次会崩溃,原因在于第一次执行完后,把p指向的对象释放了,第二次再使用该指针自然会奔溃。

    2.解决办法,引入引用计数,也就是再增加一个整数成员,当发现有人引用对象时 该值加1,如果使用完 减一,如果等于0再释放(解决过早释放问题)

    class Person {

    private:

             int count;

    public:

             void incStrong(){ count++; }     

             void decStrong(){ count--; }     

             int getStrongCount(){ return count;}

      Person() : count(0)//表示构造时count值默认值是0

      {

                       cout <<"Pserson()"<<endl;

             }

             ~Person()

             {

                       cout << "~Person()"<<endl;

             }

             void printInfo(void)

             {

                       cout<<"just a test function"<<endl;

             }

    };

    class sp

    {

    private:

             Person *p;

    public:

             sp() : p(0) {}

             sp(Person *other)

             {

                       cout<<"sp(Person *other)"<<endl;

                       p = other;

                       p->incStrong();

             }

             sp(const sp &other)

             {

                       cout<<"sp(const sp &other)"<<endl;

                       p = other.p;

                       p->incStrong();

             }

             ~sp()

             {

                       cout<<"~sp()"<<endl;

                       if (p)

                       {

                                p->decStrong();

                                if (p->getStrongCount() == 0)

                                {

                                         delete p;

                                         p = NULL;

                                }

                      }

             }

             Person *operator->()

             {

                       return p;

             }

    };

    void test_func(sp &other)

    {

             sp s = other;

             cout<<"In test_func: "<<s->getStrongCount()<<endl;

             s->printInfo();

             //Person *p = new Person();

             //p->printInfo();

             //delete p;

    }

    int main(int argc, char **argv)

    {       

             int i;

             sp other = new Person();

             cout<<"Before call test_func: "<<other->getStrongCount()<<endl;

             for (i = 0; i < 2; i++)

             {

                       test_func(other);

                       cout<<"After call test_func: "<<other->getStrongCount()<<endl;

    }

             return 0;//main函数执行完后,再次减一,这时就把person对象销毁了

    }

    3. 重载*,供下面使用

    Person& operator*()//重载*,供下面使用 

    {//不使用引用,返回的是值,就又会去构造对象(临时对象,见上述引用章节)              

    return *p;

    }

    /* 少用"Person *"; 用"sp"来代替"Person *"

    * Person *per;

    * 有2种操作: per->XXXx, (*per).XXX

    * sp也应该有这2中操作:

    * sp->XXX, (*sp).XXX*

    */

    sp other = new Person();

    (*other).printInfo();

    4.优化:将count的操作独立出去,单独为一个类,其他类如person直接继承,就可以操作count的类。

    5.优化:为了让智能指针类更通用,应当把类修改为类模版,这样就不局限于内部的指针仅指向person。

    template<typename T>

    class sp

    {

    private:

             T *p;

    }

    最终就可以实现, 用"sp<Person>"来代替"Person *

    而且不需要delete,由系统释放

    /******************************************************************************************************************/

    二、C++智能指针_轻量级指针

    count++不是原子操作,所以在多线程的情况下是不安全的

    所以引入轻量级指针(保证原子操作,保证操作count值线程安全):

    1.使用安卓提供的轻量级智能指针的代码:

    using namespace android::RSC;//使用安卓命名空间下的RSC命名空间

    class Person : public LightRefBase<Person>

    {//指定模版参数为person

    public:

             Person() {

                       cout <<"Pserson()"<<endl;

             }

             ~Person()

             {

                       cout << "~Person()"<<endl;

             }

             void printInfo(void)

             {

                       cout<<"just a test function"<<endl;

             }

    };

    2.被调用的安卓RefBase.h中的部分代码:

    template <class T>

    class LightRefBase

    {

    public:

        inline LightRefBase() : mCount(0) { }

        inline void incStrong(__attribute__((unused)) const void* id) const {

          __sync_fetch_and_add(&mCount, 1);//__sync_fetch_and_add是原子操作

    }

        inline void decStrong(__attribute__((unused)) const void* id) const {

            if (__sync_fetch_and_sub(&mCount, 1) == 1)

      {//__sync_fetch_and_sub是原子操作

                delete static_cast<const T*>(this);

             }

    }

    3.注意轻量级指针只是对count的操作是线程安全的,但是对于操作的对象即智能指针还是不安全的,多线程情况下只能自己来保证智能指针的使用是线程安全的,即智能指针指向的对象的操作是线程安全的。

    为啥叫轻量指针,是因为代码简单,使用方便。

    /******************************************************************************************************************/

    三、C++智能指针_弱指针的引入

    轻量指针,代码简单,使用方便,但是

    当有两个智能指针互相引用的时候,由于执行完引用计数不为零,就没办法释放,会造成内存泄漏:

    /* 如果对象里含有其他对象成员:

     * 构造时: 先构造其他对象成员, 再构造对象本身

     * 析构时: 顺序刚好相反

     */

    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对象的引用计数(现在此值等于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对象的引用计数(现在此值等于1)

              */

             sp<Person> son = new Person();

             /* 它是一个"=" : this->son = son

              * "="被重载, 它会再次增加该Person对象的引用计数

              * 所以son对应的Person对象的引用计数增加为2

              */

             father->setSon(son);

             /* 它是一个"=" : this->father = father

              * "="被重载, 它会再次增加该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

              */

    }

    对于交叉引用导致内存泄漏的问题,如果两个指针是弱引用,也就是不增加对方的引用计数值,那么就不会造成内存泄漏,因此只能引用弱指针(弱引用)才能解决这个问题,

    强指针(强引用):A指向B,A决定B的生死

    弱指针(弱引用):A指向B,A不能决定B的生死

    /******************************************************************************************************************/

    四、C++智能指针_强弱指针的实现与使用

    强弱指针的实现:

    加入引用计数mWeak成员并提供加减操作,同时加入flag成员表示当前是强还是弱,具体实现见安卓源码.

    1.源码说明:

    I、在RefBase.h中的class RefBase中成员指针指向weakref_impl,也就是强弱指针的实现,weakref_impl继承于weakref_type(操作成员的抽象类),也就是说weakref_impl就是具体的实现类。

    具体也可以说,

    强弱指针的实现主要是加入引用计数mWeak成员并提供加减操作,同时加入flag成员表示当前是强还是弱

    再将强弱指针的实现抽象出抽象类,具体实现类weakref_impl继承weakref_type抽象类,并在RefBase.h中的class RefBase中定义指向weakref_impl的指针。

    II、这样实现的原因:

    a.头文件中不想看到私有成员,只想看到接口,则把接口抽象出来为抽象类(放在头文件中),然后剩下的独立为实现类(放在cpp文件中),实现类继承抽象类,抽象类对外。

    b.依赖对象的时候,最好依赖指针,因为直接依赖对象,当对象发生变化就会跟着发生变化,指针只占四个字节就不会跟着变化,对空间布局没有影响

    2.强弱指针的使用:

    I、使用安卓里现成的,

    头文件:

    system/core/include/utils

    system/core/include/cutils

    cpp:

    system/core/libutils/RefBase.cpp

    makefile:

    %.o : %.cpp

             g++ -c -o $@ $< -I include //-I include表示包含当前目录下的include目录

    //查找头文件不会递归的往子目录查找,所以要么这里加要么代码中加上 目录名/XXX.h

    例:

    int main(int argc, char **argv)

    {       

             wp<Person> s = new Person();//弱指针

    //s->printInfo(); /* 出错, wp没有重载"->", "*" */

             //(*s).printInfo(); /* 出错, wp没有重载"->", "*" */

             sp<Person> s2 = s.promote();//提升为强指针,这样就可以调用了 

        if (s2 != 0) {

                       s2->printInfo();

             }

             return 0;

    }

    II、使用弱指针可以解决交叉引用导致内存泄漏的问题,但是想使用引用的对象里的函数,就必须先升级为强指针才可以调用

    #include <iostream>

    #include <string.h>

    #include <unistd.h>

    #include <utils/RefBase.h>

    using namespace std;

    using namespace android;

    class Person : public RefBase

    {//继承RefBase

    private:

             char *name;   

             wp<Person> father;

             wp<Person> son;

    public:

             Person() {

                       cout <<"Pserson()"<<endl;

             }

             Person(char *name) {

                       cout <<"Pserson(char *name)"<<endl;

                       this->name = name;

             }

             ~Person()

             {

                       cout << "~Person()"<<endl;

             }

             void setFather(sp<Person> &father)

             {

                       this->father = father;

             }

             void setSon(sp<Person> &son)

             {

                       this->son = son;

             }

             char *getName(void)

             {

                       return name;

             }

             void printInfo(void)

             {

                       sp<Person> f = father.promote();

                       sp<Person> s = son.promote();

                      

                       //cout<<"just a test function"<<endl;

                       cout<<"I am "<<name<<endl;

                       if (f != 0)

                                cout<<"My Father is "<<f->getName()<<endl;

                       if (s != 0)

                                cout<<"My Son is "<<s->getName()<<endl;

             }

    };

    /* 如果对象里含有其他对象成员:

     * 构造时: 先构造其他对象成员, 再构造对象本身

     * 析构时: 顺序刚好相反

     */

    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对象的引用计数(现在此值等于1)

              */

             sp<Person> father = new Person("LiYiShi");

             /* 1. 对于 new Person()

              * 1.1 Person对象里的father先被构造

              * 1.2 Person对象里的son被构造

              * 1.3 Person对象本身

              * 2. Person对象的指针传给"sp<Person> son"

              *    导致: sp(T* other) 被调用

              *    它增加了这个Person对象的引用计数(现在此值等于1)

              */

             sp<Person> son = new Person("LiErShi");

             father->setSon(son);

             son->setFather(father);

             father->printInfo();

             son->printInfo();

    }

    int main(int argc, char **argv)

    {       

             test_func();

             return 0;

    }

  • 相关阅读:
    求长度的另一种方法(""+obj).Length
    XCode中如何使用事务
    最终版 Reflector v1.0 (+简单的反流程混淆)
    与ObjectDataSource共舞
    性能&分布式&NewLife.XCode对无限数据的支持
    XCode之第一次亲密接触
    5,ORM组件XCode(动手)
    你知道吗?多个类多线程环境下静态构造函数的执行顺序
    使用C#编写IDA插件 IDACSharp v1.0.2010.0605
    XCMS V1.0 Beta1 发布
  • 原文地址:https://www.cnblogs.com/yuweifeng/p/7553471.html
Copyright © 2011-2022 走看看