智能指针引用计数为0后发生了什么?

来源:微信公众号「编程学习基地」

智能指针简介

为了解决C++内存泄漏的问题,C++11引入了智能指针(Smart Pointer)。在现代 c + + 编程中,标准库包含 智能指针,这些指针用于帮助确保程序不会出现内存和资源泄漏,并具有异常安全。C++11提供了三种智能指针:std::shared_ptr, std::unique_ptr, std::weak_ptr,使用时需添加头文件#include。

shared_ptr

shared_ptr 类型是 C++ 标准库中的一个智能指针,是为多个所有者可能必须管理对象在内存中的生命周期的方案设计的。shared_ptr 使用引用计数,每一个 shared_ptr 的拷贝都指向相同的内存。每引用它一次,内部的引用计数加1,每析构一次,内部的引用计数减1,减为0时,删除所指向的堆内存。shared_ptr内部的引用计数是安全的,但是对象的读取需要加锁。

智能指针对比普通指针
#include <iostream>
#include <memory>
using namespace std;
class Data
{
public:
    Data() {
        cout << "构造" << endl;
    }
    ~Data() {
        cout << "析构" << endl;
    }
};

int main()
{
    cout<<"智能指针:"<<endl;
    {
        std::shared_ptr<Data> Ptr(new Data);    //智能指针出了作用域就会被释放,引用计数减一
    }
    cout<<"普通指针:"<<endl;
    Data* ptr = new Data;
    delete ptr;
    return 0;
}
ubuntu@VM-16-5-ubuntu:~$ g++ -std=c++11 -o test test.cpp 
ubuntu@VM-16-5-ubuntu:~$ ./test 
智能指针
构造
析构
普通指针
构造
析构
基本用法

主要描述智能指针 shared_ptr 的创建,重置和管理对象的释放

#include <iostream>
#include <memory>
using namespace std;
class Data
{
public:
    Data(int id):m_id(id) {
        cout << m_id <<":构造" << endl;
    }
    ~Data() {
        cout << m_id << "析构" << endl;
    }
    int m_id; 
};
typedef std::shared_ptr<Data> DataPtr;
int main()
{
    {
        DataPtr dataPtr1 (new Data(1));              //new一个 shared_ptr 对象
        DataPtr dataPtr2 = std::make_shared<Data>(2);//构造一个 shared_ptr对象

        dataPtr1.reset();   //重置智能指针,释放其关联的指针内存
        // 首先new Data对象,然后智能指针引用计数减1,引用计数为0,故析构 Data(1),指针指针载指向 Data(3)
        dataPtr2.reset(new Data(3));
        cout<<"即将离开作用域"<<endl;
    }
    return 0;
}
ubuntu@VM-16-5-ubuntu:~$ ./test 
1:构造
2:构造
1析构
3:构造
2析构
即将离开作用域
3析构
main over
智能指针应用

主要描述智能指针的共享管理,共享之后引用计数加一, shared_ptr 的创建,获取管理的对象

#include <iostream>
#include <memory>
using namespace std;
class Data
{
public:
    Data(int id):m_id(id) {
        cout << m_id <<":构造" << endl;
    }
    ~Data() {
        cout << m_id << "析构" << endl;
    }
    int m_id; 
};
typedef std::shared_ptr<Data> DataPtr;
int main()
{
    {
        DataPtr dataPtr1(new Data(1));                         //new一个 shared_ptr 对象
        cout << "ptr1 conut:" << dataPtr1.use_count() << endl; //获取 dataPtr1 的引用计数
        {
            DataPtr dataPtr2 = dataPtr1;    // dataPtr2 和 dataPtr1 共享管理一个对象 dataPtr1 引用计数加一
            cout << "ptr1 conut:" << dataPtr1.use_count() << endl;
            cout << "ptr2 conut:" << dataPtr2.use_count() << endl;
            
            Data* pData = dataPtr2.get();   // 获取 shared_ptr 管理的对象,引用计数不变
            cout << "Data id:"<<pData->m_id << endl;
            cout<<"即将离开作用域"<<endl;
        }
        cout << "ptr1 conut:" << dataPtr1.use_count() << endl;
    }
    cout<<"main over"<<endl;
    return 0;
}
ubuntu@VM-16-5-ubuntu:~$ g++ -std=c++11 -o test test.cpp 
ubuntu@VM-16-5-ubuntu:~$ ./test 
1:构造
ptr1 conut:1
ptr1 conut:2
ptr2 conut:2
Data id:1
即将离开作用域
ptr1 conut:1
1析构
main over
智能指针引用计数为0,调用的是子类还是基类的析构?

在多肽里面,基类对象指向子类对象,对基类对象的delete操作不会执行子类析构,从而内存泄漏。那么由指针管理的基类对象(指向子类对象)的释放操作释放的是基类还是子类对象?

#include <vector>
#include <memory>
#include <iostream>
using namespace std;
class Father
{
public:
	Father(std::string name) {}
	~Father() {cout<<"父类析构"<<endl;}	//通过虚函数也可以解决这个内存泄漏问题
	std::string m_name;
};

typedef std::shared_ptr< Father > FatherPtr;

class Son:public Father
{
public:
	Son(std::string name) :Father(name) { m_name = name; }
	~Son() { cout<<"子类析构"<<endl; }
};

typedef std::shared_ptr<Father> FatherPtr;
int main()
{
    {
        FatherPtr test(new Son("deroy"));
    }
    cout<<"-------------------------"<<endl;
    Father* Ptr = new Son("test");
    delete Ptr;
    return 0;
}
ubuntu@VM-16-5-ubuntu:~$ g++ -std=c++11 -o test test.cpp 
ubuntu@VM-16-5-ubuntu:~$ ./test 
子类析构
父类析构
-------------------------
父类析构

结论:智能指针引用计数为0,基类智能指针调用的是申请对象的析构

智能指针引用计数为0,我想干件大事

引用计数为0之后我不想智能指针来帮我释放内存,我想自己释放内存可以吗?智能指针结合匿名函数综合应用。

#include <iostream>
#include <string>
#include <memory>

using namespace std;

class Person
{
public:
    Person(string name)
    {
        m_name = name;
    }
    ~Person(){
        std::cout << "父类析构:" << m_name << std::endl;
    }
    virtual void fun() { cout << m_name << ":我该干点什么事情好呢?" << endl; };

public:
    string m_name;
};
typedef std::shared_ptr<Person> PersonPtr;

class Student : public Person
{
public:
    Student(string name) : Person(name)
    {
        std::cout << "new:" << m_name << std::endl;
    }
    ~Student(){
        std::cout << "子类析构:" << m_name << std::endl;
    }
    virtual void fun() { cout << m_name << ":我该干点什么事情好呢?" << endl; };
};

PersonPtr createPerson( string name )
{
    #if 1
    PersonPtr ptr = PersonPtr(new Student(name), [](void *val) {
        Student *p = (Student *)(val);
        if (p)
        {
            cout<<"use_count=0,call lambda release function"<<endl;
            p->fun();
            delete p;
        }
    });
    #else
        PersonPtr ptr = PersonPtr(new Student(name));
    #endif
    return ptr;
}

int main()
{
    {
        PersonPtr pStu = createPerson("deroy");
        cout<<"即将离开作用域"<<endl;
    }
    cout << "main over" << endl;
    return 0;
}
ubuntu@VM-16-5-ubuntu:~$ g++ -std=c++11 -o test test.cpp 
ubuntu@VM-16-5-ubuntu:~$ ./test 
new:deroy
即将离开作用域
use_count=0,call lambda release function
deroy:我该干点什么事情好呢?
子类析构:deroy
父类析构:deroy
main over
注意事项
  1. 智能指针管理的是堆上面的指针,(栈上面的地址会造成两次调用析构)
  2. shared_ptr相当于一个指针,拷贝和赋值会是的引用加一
std::shared_ptr<Data> dataPtr1(new Data(1));	// Data(1)的引用计数为1
std::shared_ptr<Data> dataPtr2 = dataPtr1;		//现在dataPtr1和dataPtr2同时指向Person(1),的引用计数加一
std::cout<<dataPtr1.use_count()<<std::endl;	//引用计数为2
std::cout<<dataPtr2.use_count()<<std::endl;	//引用计数为2
  1. 只有当引用计数为0时,才会释放内存
/*接上面的代码*/
dataPtr1.reset();	//Data(1)的引用计数为1
//p3.reset();//Data(1)的引用计数为0,Data(1)
  1. 不要用一个原始指针初始化多个shared_ptr,原因在于,会造成二次销毁,如下所示:
#include <iostream>
#include <memory>
using namespace std;
class Person
{
public:
    Person(int v) {
        value = v;
        std::cout << "Cons" <<value<< std::endl;
    }
    ~Person() {
        std::cout << "Des" <<value<< std::endl;
    }
    int value;
};

int main()
{
    Person *p5 = new Person(5);
    std::shared_ptr<Person> p6(p5);
    std::shared_ptr<Person> p7 = p6; //使用赋值共享指针对象的管理
    // std::shared_ptr<Person> p7(p5);// logic error
    return 0;
}
ubuntu@VM-16-5-ubuntu:~$ g++ -std=c++11 -o test test.cpp 
ubuntu@VM-16-5-ubuntu:~$ ./test 
Cons5
Des5
  1. 避免循环引用

后面 weak_ptr 介绍。

智能指针相关的函数总结
成员函数作用
reset()重置智能指针,delete其关联的指针。
release()不delete关联指针,并返回关联指针。 释放关联指针的所有权,智能指针为空。
get()仅仅返回关联指针
use_count()获取引用计数
std作用
std::make_shared创建 make_shared 对象C++14
std::move()对象转移
  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

DeRoy

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值