智能指针循环引用--转

  在智能指针中shared_ptr用来计算指向对象的指针的个数,但是如果先执行shared_ptr1=shared_ptr2,再执行shared_ptr2=shared_ptr1,这样shared_ptr1.count()和shared_ptr2.count()都为1,这就造成了循环引用,循环引用会导致堆内存无法正确释放,导致内存泄露。

  考虑一个简单的对象——家长与子女:a Parent has a Child,a Child Knows his parent。在C++中,如果使用原始指针作为成员,Child和Parent由谁释放?如何保证指针的有效性?如何防止出现悬空指针?这里就需要利用智能指针去实现,智能指针可以将对象语义转变为值语义,shared_ptr能轻松的解决生命周期的问题,不必担心悬空指针。但是这个模型存在循环引用的问题,就需要利用weak_ptr。

原始指针的做法(容易出错):

#include<bits/stdc++.h>
using namespace std;

class Parent;
class Child;

class Parent{
private:
    Child* myChild;
public:
    void setChild(Child* ch){
        this->myChild=ch;
    }
    void dosomething(){
        if(this->myChild){
            cout<<"Parent"<<endl;
        }
    }
    ~Parent(){
        delete myChild;
    }
};

class Child{
private:
    Parent* myParent;
public:
    void setParent(Parent* p){
        this->myParent=p;
    }
    void dosomething(){
        if(this->myParent){
            cout<<"Child"<<endl;
        }
    }
    ~Child(){
        delete myParent;
    }
};

int main(){

    {
        Parent *p=new Parent;
        Child *c=new Child;
        p->setChild(c);
        c->setParent(p);
        p->dosomething();   //Parent
        c->dosomething();   //Child
        delete c; //仅删除这一个
//        p->dosomething(); 不能访问
    }
    return 0;
}

循环引用内存泄露的写法:

#include<bits/stdc++.h>
using namespace std;

class Parent;
class Child;

class Parent{
private:
    shared_ptr<Child> myChild;
public:
    void setChild(shared_ptr<Child> ch){
        this->myChild=ch;
    }
    void dosomething(){
        if(this->myChild.use_count()){
            cout<<"Parent"<<endl;
        }
    }
    ~Parent(){
    }
};

class Child{
private:
    shared_ptr<Parent> myParent;
public:
    void setParent(shared_ptr<Parent> p){
        this->myParent=p;
    }
    void dosomething(){
        if(this->myParent.use_count()){
            cout<<"Child"<<endl;
        }
    }
    ~Child(){
    }
};

int main(){
    weak_ptr<Parent> wpp;
    weak_ptr<Child> wpc;
    {
        shared_ptr<Parent> p(new Parent);
        shared_ptr<Child> c(new Child);
        p->setChild(c);     cout<<p.use_count()<<endl;//1     cout<<c.use_count()<<endl;//2
        c->setParent(p);
        cout<<p.use_count()<<endl;//2
        cout<<c.use_count()<<endl;//2
        wpp=p;
        wpc=c;
    }
    //超出该域,p、c都已被析构,但是weak_ptr显示p、c的引用都为1
    cout<<wpp.use_count()<<endl;//1
    cout<<wpc.use_count()<<endl;//1
    return 0;
}

正确做法是:

#include<bits/stdc++.h>
using namespace std;

class Parent;
class Child;

class Parent{
private:
    weak_ptr<Child> myChild;
public:
    void setChild(shared_ptr<Child> ch){
        this->myChild=ch;
    }
    void dosomething(){
        if(this->myChild.lock()){
            cout<<"Parent"<<endl;
        }
    }
    ~Parent(){
    }
};

class Child{
private:
    shared_ptr<Parent> myParent;
public:
    void setParent(shared_ptr<Parent> p){
        this->myParent=p;
    }
    void dosomething(){
        if(this->myParent.use_count()){
            cout<<"Child"<<endl;
        }
    }
    ~Child(){
    }
};

int main(){
    weak_ptr<Parent> wpp;
    weak_ptr<Child> wpc;
    {
        shared_ptr<Parent> p(new Parent);
        shared_ptr<Child> c(new Child);
        p->setChild(c);
        cout<<p.use_count()<<endl;//1
        cout<<c.use_count()<<endl;//1
        c->setParent(p);
        cout<<p.use_count()<<endl;//2
        cout<<c.use_count()<<endl;//1
        wpp=p;
        wpc=c;
    }

    cout<<wpp.use_count()<<endl;//0
    cout<<wpc.use_count()<<endl;//0
    return 0;
}

  下面是一个简单智能指针的demo。智能指针类将一个计数器与类指向的对象相关联,引用计数跟踪该类有多少个对象共享同一指针。每次创建类的新对象时,初始化指针并将引用计数置为1;当对象作为另一对象的副本而创建时,拷贝构造函数拷贝指针并增加与之相应的引用计数;对一个对象进行赋值时,赋值操作符减少左操作数所指对象的引用计数(如果引用计数为减至0,则删除对象),并增加右操作数所指对象的引用计数;调用析构函数时,构造函数减少引用计数(如果引用计数减至0,则删除基础对象)。智能指针就是模拟指针动作的类。所有的智能指针都会重载 -> 和 * 操作符。智能指针还有许多其他功能,比较有用的是自动销毁。这主要是利用栈对象的有限作用域以及临时对象(有限作用域实现)析构函数释放内存。

#include<bits/stdc++.h>
using namespace std;

template<typename T>
class smartPointer{
private:
    T* _ptr;
    size_t* _count;
public:
    smartPointer(T* ptr=nullptr):_ptr(ptr){//构造函数
        if(_ptr){
            _count=new size_t(1);
        }
        else{
            _count=new size_t(0);
        }
    }

    smartPointer(const smartPointer& ptr){//拷贝构造函数
        if(this !=&ptr){
            this->_ptr=ptr._ptr;
            this->_count=ptr._count;
            (*this->_count)++;
        }
    }

    smartPointer& operator =(const smartPointer& ptr){//赋值构造函数
        if (this->_ptr == ptr._ptr) {
            return *this;
        }

        if(this->_ptr){
            (*this->_count)--;
            if(*this->_count==0){
                delete this->_ptr;
                delete this->_count;
            }
        }

        this->_ptr=ptr._ptr;
        this->_count=ptr._count;
        (*this->_count)++;
        return *this;
    }

    T& operator *(){//重载操作符
        assert(this->_ptr==nullptr);
        return this->_ptr;
    }

    T& operator->(){//重载操作符
        assert(this->_ptr==nullptr);
        return this->_ptr;
    }

    ~smartPointer(){//析构函数
        (*this->_count)--;
        if(*this->_count==0){
            delete this->_ptr;
            delete this->_count;
        }
    }

    size_t use_count(){
        return *this->_count;
    }
};

int main()
{
    {
        smartPointer<int> sp(new int(10));
        smartPointer<int> sp2(sp);
        smartPointer<int> sp3(new int(20));
        sp=sp3;
        cout<<sp.use_count()<<endl;//1
        cout<<sp2.use_count()<<endl;//2
        cout<<sp3.use_count()<<endl;//2
    }
    {
        shared_ptr<int> sp(new int(10));
        shared_ptr<int> sp2(sp);
        shared_ptr<int> sp3(new int(20));
        sp=sp3;
        cout<<sp.use_count()<<endl;//1
        cout<<sp2.use_count()<<endl;//2
        cout<<sp3.use_count()<<endl;//2
    }
    return 0;
}

参考地址:https://www.cnblogs.com/wxquare/p/4759020.html、《C++ primer》

原文地址:https://www.cnblogs.com/ybf-yyj/p/9661988.html

时间: 2024-10-10 03:36:33

智能指针循环引用--转的相关文章

[C++] 智能指针的引用计数如何实现?—— 所有该类的对象共享静态类成员变量

问:智能指针可以对指针的引用数量进行计数,一个智能指针释放时,别的智能指针怎么知道的? 这是一个脑筋急转弯题.... 不像普通的变量,静态成员变量是被所有类对象共享的,不同的对象可以访问对方的该静态成员变量,因此静态成员变量和类对象并没有联系. The static keyword has another meaning when applied to global variables -- it gives them internal linkage (which restricts them

C++11智能指针和引用

最近在学习课程的时候发现一个很困惑的问题,上代码 class DataHeader; class LoginResult:public DataHeader; typedef std::shared_ptr<DataHeader> DataHeaerPtr; //原型 void addSendTask(ClientSocketPtr& pClient, DataHeaderPtr& header) //未报错调用 DataHeaderPtr ret = std::make_sh

Effective C++ .13使用智能指针来引用资源

#include <iostream> #include <cstdlib> #include <memory> using namespace std; class Kiwi { private: int weight; public: Kiwi(int w) : weight(w) {} ~Kiwi() { cout<<"~Kiwi"<<endl; } int getWeight() {return weight;} };

引用计数的智能指针的实现

引用计数的智能指针是对<Effective C++ 条款13:以对象管理资源>的一个实现. 我们要设计一个智能指针,使他能够管理资源,在正确的实际调用资源的析构函数. 首先我们需要一个指针reference来指向资源,当智能指针构造时,用reference指针指向资源,在我们确定资源应该被析构时,我们对reference指针进行delete. 如果只有reference指针的话,只能实现出auto_ptr的效果,我们还需要添加引用计数系统counter来统计指向资源的智能指针的个数.count

(转)Delphi2009初体验 - 语言篇 - 智能指针(Smart Pointer)的实现

快速导航 一. 回顾历史二. 智能指针简介三. Delphi中的interface四. Delphi中智能指针的实现五. interface + 泛型 = 强类型的智能指针!六. 智能指针与集合七. 注意事项八. 总结 本随笔所有源代码打包下载 一.回顾历史 在c++中,对象可以创建在栈里,也可以创建在堆里.如: class CTestClass{public: CTestClass() { printf("Create"); } void DoPrint() {} ~CTestCla

Chromium和WebKit的智能指针实现原理分析

C++不像Java一样,由虚拟机负责对象分配和释放.也就是说,开发人员使用C++编写代码时,要自己负责对象分配和释放.WebKit和Chromium都是使用C++开发的,因此它们也面临上述问题.在解决对象释放问题时,要做到在对象不需要时自动释放,因为手动释放会带来忘记释放或者释放后又继续使用的隐患.智能指针是实现对象自动释放的有效技术手段.本文就分析Chromium和WebKit的智能指针的实现. 老罗的新浪微博:http://weibo.com/shengyangluo,欢迎关注! 在现实中,

Binder学习笔记(十一)—— 智能指针

轻量级指针 Binder的学习历程爬到驱动的半山腰明显感觉越来越陡峭,停下业务层的学习,补补基础层知识吧,这首当其冲的就是智能指针了,智能指针的影子在Android源码中随处可见.打开frameworkds/rs/cpp/util,RefBase.h和StrongPointer.h两个文件,代码多读几遍都能读懂,可是串起来总感觉摸不到骨架,把不住主线.闭上眼零零星星的点串不成一条线.究其原因应该是此处使用了模式,最好先剔除掉业务层的皮肉,把模式的骨架摸个门清,再回来看代码就会势如破竹了. 不是多

RAII和模拟实现智能指针

什么叫RAII(Resource Acquisition Is Initialization)? RAII指的就是资源分配即初始化,它的作用就是定义一个类来封装资源的分配和释放,在构造函数完成资源的分配和初始化,在析构函数完成资源的清理,可以保证资源的正确初始化和释放.它是一种技术. 为什么要使用RAII? 在堆上分配空间时,我们必须很仔细的申请并给出相应的释放语句,但是随着程序的复杂度增大,判断.循环.递归这样的语句会让程序走向不确定,很有可能出现申请了没释放,申请了多次释放.所以我们定义了一

四种智能指针:auto_ptr,unique_ptr,shared_ptr,weak_ptr

stl中auto_ptr,unique_ptr,shared_ptr,weak_ptr四种智能指针的使用总结 (1)auto_ptr 主要用于解决资源自动释放的问题.防止用户忘记delete掉new申请的内存空间.使用auto_ptr会在离开变量的作用域之后直接调用析构函数进行资源释放. void Function() { auto_ptr<Obj> ptr(new Obj(20)); ... if (error occur) throw exception... } 但是,这是一种被c++1