栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > C/C++/C#

【C++】智能指针如何实现引用计数?用static计数会产生什么问题?

C/C++/C# 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

【C++】智能指针如何实现引用计数?用static计数会产生什么问题?

1. 为什么需要智能指针?

在C++中,动态内存的管理是通过一对运算符来完成的:new 在动态内存中为对象分配空间并返回一个指向该对象的指针;delete 接受一个动态对象的指针,销毁该对象,并释放与之关联的内存。

动态内存的使用很容易出问题,比如new了一个变量,忘记delete,就会产生内存泄漏问题;有2个指针指向同一块内存,第一个指针成功delete与之关联的内存,那剩下那么指针再次delelte同一块内存,就会产生错误。

为了更容易地使用动态内存,C++11标准库提供了两种智能指针类型来管理动态对象。shared_ptr允许多个指针指向同一个对象,也称共享指针;unique_ptr则独占所指向的指针,也称独占指针。标准库还定义了一个名为wek_ptr的伴随类,它是一种弱定义,指向shared_ptr所管理的对象,它是为了解决共享指针循环引用的问题,该问题不是此文的重点。

2. shared_ptr指针的使用

智能指针也是模板,当创建智能指针时,必须提供指向的类型。

语法功能
shared_ptr p可以指向T类型的智能指针
p将将p用作一个条件判断,若p指向一个对象,则为true
*p解引用p,获得它指向的对象
p->mem等价于(*p).mem
3.shared_ptr指针的引用计数

当进行拷贝和赋值操作时,每个shared_ptr都会记录有多少个其他shared_ptr指向相同的对象。在每个shared_ptr都有一个关联的计数器,通常称其为引用计数。无论何时我们拷贝一个shared_ptr,计数器都会递增。例如,当用一个shared_ptr初始化另一个shared_ptr,或将它作为参数传递给一个函数以及作为函数的返回值吗,它所关联的计数器就会递增。当我们给shared_ptr赋予一个新值或是shared_ptr被销毁,计数器就会递减。当shared_ptr的计数器变为0,它就会自动释放自己管理的对象。

到底是用一个计数器还是其他数据结构来记录有多少指针共享对象,完全由标准库的具体实现来决定。关键是智能指针类能记录有多少个shared_ptr指向相同的对象,并能在恰当的时候自动释放对象。

4. 为什么不能用static计数?
static int count;

有些同学可能想要在智能指针类中添加一个static成员变量,每新增一个智能指针类,count就加1 。在只有一个对象的时候,该方法是合适的。但是static变量是属于类的,如下图所示,当出现第二个对象,此时新增一个shared_ptr指针,此时内部的count就变成了4,既不满足指向对象1的指针数量,也不满足指向对象2的指针数量。因此我们需要指向对象1的shared_ptr共享一个内部计数器,指向对象2的shared_ptr共享另外一个内部计数器,实现途径见下一节。

5. 实现简易版内部技术器 5.1 自定义类声明

定义一个myclass类,里面包含一个num成员变量和一个getNum函数。

class myclass {
private:
    int num;

public:
    myclass(int n) :num(n) { cout << "成功构成一个myclass类" << endl; }

    int getNum() const{
        return num;
    }
    ~myclass(){ cout << "成功析构一个myclass类" << endl; }
};
5.2 辅助类

定义一个辅助类,负责实现计数功能,里面的变量和函数均为私有类型,不能被其他类所访问。为了让智能指针类可以访问该类,要声明SmartPtr类为友元类。这个类含有一个计数器count和一个指向myclass的指针。

类可以允许其他类或函数访问它的非公有成员,方法是其他类或者函数成为它的友元,前面加上关键字friend。如果一个类指定了友元类,则友元类的成员函数可以访问此类包含非公有成员在内的所有成员。

class countClass {
private:
    friend class SmartPtr;

    countClass(myclass* ptr) :p(ptr), count(1) { cout << "成功构成一个countClass类,此时count等于1" << endl; }
    ~countClass() {
        cout << "进入countClass析构函数" << endl;
        delete p;
        cout << "成功释放p" << endl;
    }

    int count;
    myclass* p;
};
5.3 智能指针类

智能指针类SmartPtr只在初始化构造时new一个countClass类负责计数,此时count等于1 。当调用拷贝构造函数时,相当于又多了一个智能指针指向该对象,因此计数器加1 。 使用拷贝运算符时,等于将右边指向的对象拷贝给左边,因此左边的计数器得减1,因为它不指向原先的对象了,右边对应的技术器得加1,因为新增了一个智能指针指向该对象。

在析构函数中,要先判断计数器是否为0,只有为0,才去释放内存。

class SmartPtr {
private:
    countClass* countPtr;

public:
    //初始化构造函数
    SmartPtr(myclass* ptr): countPtr(new countClass(ptr)){ cout << "成功构成一个SmartPtr类" << endl; };

    //拷贝构造函数
    SmartPtr(const SmartPtr& sp) :countPtr(sp.countPtr) { ++countPtr->count; cout << "调用拷贝构造函数,计数器加1" << endl;
    }

    //拷贝赋值运算符
    SmartPtr& operator=(const SmartPtr& rhs) {
        ++rhs.countPtr->count;
        cout << "调用拷贝运算符,右边ptr计数器加1,左边ptr计数器减1" << endl;
        if (--countPtr->count == 0) {
            cout << "左边ptr计数器为0,释放countPtr" << endl;
            delete countPtr;
        }
            
        countPtr = rhs.countPtr;
        return *this;
    }

    //析构函数
    ~SmartPtr() {
        if (--countPtr->count == 0) {
            delete countPtr;
            cout << "此时计数为0,成功释放countPtr" << endl;
        }  else
            cout << "计数器减1,还有" << countPtr->count << "个指针指向对象,不能释放" << endl;
    }
};
6. 测试
    myclass* mp = new myclass(10);
    {
        SmartPtr ptr1(mp);
        cout << mp->getNum() << endl;
    }
    cout << mp->getNum() << endl;


分析一下输出,在构造智能指针类之前必须先构造一个辅助类作为计数器,所以可以看到countClass类是比SmartPtr类更早构造出来的。因为ptr1处于一个局部函数体,所以当函数退出大括号时,该ptr1会去检查count是否等于0,是的话就去析构mp。等出了大括号,再去访问mp的变量,可以发现已经是随机值,代表mp已经被成功稀释了。

    myclass* mp = new myclass(10);
    {
        cout << "进入第一个括号" << endl;
        SmartPtr ptr1(mp);
        {
            cout << "进入第二个括号" << endl;
            SmartPtr ptr2(ptr1);
            cout << "退出第二个括号" << endl;
        }
        cout << "退出第一个括号" << endl;
    }


可以看到进入第二个括号后,调用了拷贝构造函数,计数器加1,此时就算退出第二个括号,因此还有一个指针指向对象,所以对象并不会释放。
、

《C++ Primer》
C++ 引用计数技术及智能指针的简单实现

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/1026179.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号