前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >智能指针引用计数为0后,发生了什么?

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

作者头像
DeROy
发布2021-06-01 15:44:28
2K0
发布2021-06-01 15:44:28
举报
文章被收录于专栏:编程学习基地

智能指针简介

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

shared_ptr

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

智能指针对比普通指针

智能指针的特殊之处在于帮助编程开发人员管理内存,确保程序不会出现内存和资源泄漏,并具有异常安全。

代码语言:javascript
复制
#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;
}
代码语言:javascript
复制
ubuntu@VM-16-5-ubuntu:~$ g++ -std=c++11 -o test test.cpp 
ubuntu@VM-16-5-ubuntu:~$ ./test 
智能指针
构造
析构
普通指针
构造
析构

从输出可以看出智能指针不需要手动释放,出了作用域之后就自动释放了,也就是说智能指针生命周期结束之后就会自动释放内存。而普通指针需要手动释放,不然会造成内存泄露。

基本用法

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

代码语言:javascript
复制
#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;
}
代码语言:javascript
复制
ubuntu@VM-16-5-ubuntu:~$ ./test 
1:构造
2:构造
1析构
3:构造
2析构
即将离开作用域
3析构
main over

智能指针应用

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

代码语言:javascript
复制
#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;
}
代码语言:javascript
复制
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操作不会执行子类析构,从而造成内存泄漏。那么由指针管理的基类对象(指向子类对象)的释放操作释放的是基类还是子类对象?

代码语言:javascript
复制
#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;
}
代码语言:javascript
复制
ubuntu@VM-16-5-ubuntu:~$ g++ -std=c++11 -o test test.cpp 
ubuntu@VM-16-5-ubuntu:~$ ./test 
子类析构
父类析构
-------------------------
父类析构

从输出上来看,智能指针 shared_ptr 管理的基类对象(指向子类对象)的释放操作释放的是子类对象,不会造成内存泄露

智能指针引用计数为0,你想干啥?

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

代码语言:javascript
复制
#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;
}
代码语言:javascript
复制
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相当于一个指针,拷贝和赋值会是的引用加一
代码语言:javascript
复制
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时,才会释放内存
代码语言:javascript
复制
/*接上面的代码*/
dataPtr1.reset(); //Data(1)的引用计数为1
//dataPtr2.reset();//Data(1)的引用计数为0,Data(1)
  1. 不要用一个原始指针初始化多个shared_ptr,原因在于,会造成二次销毁,如下所示:
代码语言:javascript
复制
#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;
}
代码语言:javascript
复制
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()

对象转移

·················END·················

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2021-05-17,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 编程学习基地 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 智能指针简介
  • shared_ptr
    • 智能指针对比普通指针
      • 基本用法
        • 智能指针应用
          • 智能指针引用计数为0,调用子类还是基类的析构?
            • 智能指针引用计数为0,你想干啥?
              • 注意事项
                • 智能指针相关的函数
                领券
                问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档