c++ 学习系列 -- 智能指针

这篇具有很好参考价值的文章主要介绍了c++ 学习系列 -- 智能指针。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

一   为什么引入智能指针?解决了什么问题?

C++ 程序设计中使用堆内存是非常频繁的操作,堆内存的申请和释放都由程序员自己管理。但使用普通指针,容易造成内存泄露(忘记释放)、二次释放、程序发生异常时内存泄露等问题等。

另外,使用普通指针容易产生 野指针、悬空指针 等问题。

所以 C++11 就引入了智能指针来管理内存。

二  常用的智能指针与区别

常用智能指针有  shared_ptrunique_ptr weak_ptr 

  • unique_ptr: 独占式指针,同一时刻只允许有一个 unique_ptr 指针指向一个对象
  • shared_ptr: 共享式指针,同一时刻允许多个 shared_ptr 指针指向同一个对象。
    •  缺点:出现相互引用时,容易导致死锁或者内存无法释放内存的问题
  • weak_ptr: 为了解决 shared_ptr 相互引用可能导致的死锁或无法释放内存的问题而引入,通常与shared_ptr 配合使用。但是由于缺少引用计数,一旦最后一个指向对象的shared_ptr被销毁,对象就会被释放。即使有weak_ptr指向对象,对象也还是会被释放。 C++11智能指针(weak_ptr) - 简书 (jianshu.com)

三  常用智能指针使用例子

   1  unique_ptr 例子

        unique_ptr 是一个独享所有权的智能指针

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

class Person
{
public:
    Person(std::string name):m_name(name)
    {
        std::cout << "Person constructor name: " << m_name << std::endl;
    }

    ~Person()
    {
        std::cout << "Person destructor name: " << m_name << std::endl;
    }

private:
    std::string m_name;
};

#include<memory>



void testUniquePtr()
{
    std::unique_ptr<Person>  p1_ptr(new Person("P1 ---"));
    std::unique_ptr<Person>  p2_ptr = std::move(p1_ptr);

   // std::unique_ptr<Person>  p3_ptr = p2_ptr; // 编译不过
   // std::unique_ptr<Person>  p4_ptr(p2_ptr); // 编译不过

}


int main(int argc, char *argv[])
{
    testUniquePtr();
    return 0;
}

   输出

c++ 学习系列 -- 智能指针,c++,学习,开发语言

2   shared_ptr 例子

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

using namespace std;

void testSharedPtr()
{
        shared_ptr<string> pa(new string("PAAAA"));
        shared_ptr<string> pb(new string("PBBBB"));
        cout << "*pa " << *pa << endl;//CHN
        cout << "pa.use_count " << pa.use_count() << endl;//1
        cout << "*pb " << *pb << endl;//USA
        cout << "pb.use_count " << pb.use_count() << endl;//1

        pa = pb;
        cout << *pa << endl;//USA
        cout << "pa.use_count " << pa.use_count() << endl;//2:pa和pb指向同一个资源USA了,该资源的计数为2,所以pb、pb都输出2
        cout << "pb.use_count " << pb.use_count() << endl;//2

        pa.reset();
        pb.reset();
        cout << "pa.use_count " << pa.use_count() << endl;//0
        cout << "pb.use_count " << pb.use_count() << endl;//0
}

int main(int argc, char *argv[])
{

    testSharedPtr();
    return 0;
}

   3  weak_ptr 例子

       3.1  shared_ptr 相互引用的问题

// a.h
#include<memory>

class B;

class A
{
public:
    A()
   {
       std::cout << "A constructor ---" << std::endl;
   }

    ~A()
   {
    std::cout << "A destructor ---" << std::endl;
   }

public:
    std::shared_ptr<B> m_b_ptr;
};

// b.h
#include<memory>

class A;

class B
{
public:
    B()
    {
       std::cout << "A constructor ---" << std::endl;
   }
    ~B()
    {
       std::cout << "A destructor ---" << std::endl;
   }

public:
    std::shared_ptr<A> m_a_ptr;
};


// main.cpp
void testSharedPtr()
{
    std::shared_ptr<A> pa(new A);
    cout << "pa.use_count " << pa.use_count() << endl;//1

    std::shared_ptr<B> pb(new B);
    cout << "pb.use_count " << pb.use_count() << endl;//1

    pa->m_b_ptr = pb;
    cout << "pb.use_count " << pb.use_count() << endl;//2
    cout << "pa.use_count " << pa.use_count() << endl;//1
    pb->m_a_ptr = pa;
    //由于share_ptr是共享资源,所以pb所指向的资源的引用计数也会加1
    cout << "pb.use_count " << pb.use_count() << endl;//2
    cout << "pa.use_count " << pa.use_count() << endl;//2

}

int main(int argc, char *argv[])
{
    testSharedPtr();

    return 0;
}


  输出 :     

c++ 学习系列 -- 智能指针,c++,学习,开发语言

通过输出可以看到未执行析构函数,存在内存泄漏。

引用计数分别增加到了 2 ,不为 0 就意味着无法释放内存。

3.2  weak_ptr 与 share_ptr 使用 

// a.h
#include<memory>

class B;

class A
{
public:
    A()
   {
       std::cout << "A constructor ---" << std::endl;
   }

    ~A()
   {
    std::cout << "A destructor ---" << std::endl;
   }

public:
    std::weak_ptr<B> m_b_ptr;
};

// b.h
#include<memory>

class A;

class B
{
public:
    B()
    {
       std::cout << "A constructor ---" << std::endl;
   }
    ~B()
    {
       std::cout << "A destructor ---" << std::endl;
   }

public:
    std::shared_ptr<A> m_a_ptr;
};


// main.cpp
void testWeakPtr()
{
 std::shared_ptr<A> pa(new A);
    cout << "pa.use_count " << pa.use_count() << endl;//1

    std::shared_ptr<B> pb(new B);
    cout << "pb.use_count " << pb.use_count() << endl;//1

    pa->m_b_ptr = pb;
    cout << "pb.use_count " << pb.use_count() << endl;//1
    cout << "pa.use_count " << pa.use_count() << endl;//2
    pb->m_a_ptr = pa;
    
    cout << "pb.use_count " << pb.use_count() << endl;//1  由于 weak_ptr 是弱引用,不会增加引用计数
    cout << "pa.use_count " << pa.use_count() << endl;//2  由于share_ptr是共享资源,所以pb所指向的资源的引用计数也会加1


}

int main(int argc, char *argv[])
{
    testWeakPtr();

    return 0;
}


输出:

c++ 学习系列 -- 智能指针,c++,学习,开发语言

 通过输出可以看到执行析构函数,不存在内存泄漏。

//  资源B的引用计数一直就只有1,当pb析构时,B的计数减一,变为0,B得到释放,
//  B释放的同时也会使A的计数减一,同时pa自己析构时也会使资源A的计数减一,那么A的计数为0,A得到释放。

四  智能指针的原理与简单实现

智能指针实际运用的就是c++ 中的 RAII 技术,详情见  C++ 学习系列 二 -- RAII 机制_在河之洲木水的博客-CSDN博客

1. unique_ptr 

 因为是独占型指针,不可以拷贝与赋值,所以需要禁止拷贝构造函数与赋值函数

// my_nuique_ptr.h
template<typename T>
class my_unique_ptr
{
public:
    my_unique_ptr(T* ptr = nullptr);
    ~my_unique_ptr();
    my_unique_ptr(my_unique_ptr&& other_ptr); // c++ 中声明移动构造函数后,则自动禁用拷贝构造函数
    my_unique_ptr& operator=(my_unique_ptr&& other_ptr); // c++ 中声明移动赋值函数后,则自动禁用拷贝赋值函数

    T& operator*() const; // 指针的基本操作,取值
    T* operator->() const;
    operator bool() const; // 提供一个本类型到bool的隐式转换,不允许使用参数

private:
    T* m_ptr;
};

template<typename T>
my_unique_ptr<T>::my_unique_ptr(T* ptr):m_ptr(ptr)
{

}

template<typename T>
my_unique_ptr<T>::~my_unique_ptr()
{
    delete m_ptr;
}

template<typename T>
my_unique_ptr<T>::my_unique_ptr(my_unique_ptr&& other_ptr)
{
    this->m_ptr = other_ptr.m_ptr;
    other_ptr.m_ptr = nullptr;
}

template<typename T>
my_unique_ptr<T>&
my_unique_ptr<T>::operator=(my_unique_ptr&& other_ptr)
{
    this->m_ptr = other_ptr.m_ptr;
    other_ptr.m_ptr = nullptr;

    return this;
}

template<typename T>
T&   my_unique_ptr<T>::operator*() const
{
    return *m_ptr;
}

template<typename T>
T* my_unique_ptr<T>::operator->() const
{
    return m_ptr;
}

template<typename T>
my_unique_ptr<T>::operator bool() const
{
    return m_ptr;
}



// main.cpp

#include<iostream>
#include"my_unique_ptr.h"

void testFunc2()
{
    my_unique_ptr<Person> my_ptr(new Person("p1 ------"));
    std::cout << "person name1: "<<my_ptr->getName() << std::endl;
    my_unique_ptr<Person> my_ptr2(std::move(my_ptr));
    std::cout << "person name2: "<<my_ptr2->getName() << std::endl;

     //my_unique_ptr<Person> my_ptr3 = my_ptr; // 编译失败
    // my_unique_ptr<Person> my_ptr4(my_ptr); // 编译失败

}


int main()
{
   testFunc2();

   return 0;
}

输出:

c++ 学习系列 -- 智能指针,c++,学习,开发语言

2. shared_ptr

shared_ptr 是共享型指针,同一时刻可以右多个指针指向同一个对象,只有最后一个指针离开作用域时,才会调用对象的析构函数,释放对象中的资源。

那么是如何实现的呢?

答案是:利用引用计数法。在 类 shared_ptr 中定义一个成员变量引用计数 share_count ,当有一个指针指向相同的对象时,就将 share_count 就自增 1,为了各 shared_ptr 的引用计数 share_count 同时增加,可以将 share_count 手动开辟一个空间,用普通指针指向它。

若是考虑到多线程的场景,还应该将 引用计数 share_count 加上锁才可以。

// my_shared_ptr.h
#include <mutex>
static std::mutex gMutex;


template<typename T>
class my_shared_ptr
{
public:
    my_shared_ptr(T* ptr = nullptr);
    ~my_shared_ptr();
    my_shared_ptr(my_shared_ptr& other_ptr);
    my_shared_ptr& operator=(my_shared_ptr& other_ptr);

    T& operator*();
    T* operator->();

    int user_count();

private:

    void addCount();
    void minusCount();

    T* m_ptr;
    int* share_count = nullptr;
};


template<typename T>
my_shared_ptr<T>::my_shared_ptr(T* ptr):m_ptr(ptr)
{
    if(!share_count)
    {
        share_count = new int(1);
    }
}

template<typename T>
my_shared_ptr<T>::~my_shared_ptr()
{
    minusCount();
    if((*this->share_count) == 0 && m_ptr)
        delete m_ptr;
}

template<typename T>
my_shared_ptr<T>::my_shared_ptr(my_shared_ptr& other_ptr)
{
    this->m_ptr = other_ptr.m_ptr;
    this->share_count = other_ptr.share_count;
    addCount();
}

template<typename T>
my_shared_ptr<T>& my_shared_ptr<T>::operator=(my_shared_ptr& other_ptr)
{
    this->m_ptr = other_ptr.m_ptr;
    this->share_count = other_ptr.share_count;
    addCount();
    return *this;
}

template<typename T>
T&  my_shared_ptr<T>::operator*()
{
    return *this->m_ptr;
}

template<typename T>
T*  my_shared_ptr<T>::operator->()
{
    return this->m_ptr;
}

template<typename T>
void my_shared_ptr<T>::addCount()
{
     std::lock_guard<std::mutex> guard(gMutex);
    (*this->share_count)++;
}

template<typename T>
void my_shared_ptr<T>::minusCount()
{
     std::lock_guard<std::mutex> guard(gMutex);
    (*this->share_count)--;
}

template<typename T>
int my_shared_ptr<T>::user_count()
{
    return *this->share_count;
}


// person.h
#include<string>

class Person
{
public:
    Person(std::string name);
    Person(const Person& p);
    ~Person();

    std::string& getName();

private:
    std::string m_name;
};


// person.cpp
#include "person.h"
#include<iostream>
Person::Person(std::string name):m_name(name)
{
    std::cout << "Person constructor name: " << m_name << std::endl;
}

Person::Person(const Person& p)
{
    this->m_name = p.m_name;
    std::cout << "Person  copy constructor name: " << this->m_name << std::endl;
}

Person::~Person()
{
    std::cout << "Person destructor name: " << m_name << std::endl;
}

std::string& Person::getName()
{
    return m_name;
}




// main.cpp

void  testMySharedPtr()
{
    my_shared_ptr<Person>  ptr1(new Person("ptr1 ---"));
    std::cout << "ptr1 user_count: " << ptr1.user_count() << std::endl;

    my_shared_ptr<Person> ptr2(ptr1);
    std::cout << "ptr1 user_count: " << ptr1.user_count() << std::endl;
    std::cout << "ptr2 user_count: " << ptr2.user_count() << std::endl;

    my_shared_ptr<Person> ptr3 = ptr2;
    std::cout << "ptr1 user_count: " << ptr1.user_count() << std::endl;
    std::cout << "ptr2 user_count: " << ptr2.user_count() << std::endl;
    std::cout << "ptr3 user_count: " << ptr3.user_count() << std::endl;

}

int main()
{
    testMySharedPtr();

    return 0;
}

输出

c++ 学习系列 -- 智能指针,c++,学习,开发语言

 文章来源地址https://www.toymoban.com/news/detail-647572.html

3. weak_ptr

前面提到过,weak_ptr 是与 shared_ptr 配合使用的,weak_ptr 无引用计数。

// my_weak_ptr.h
template<typename T>
class my_weak_ptr
{
public:
    my_weak_ptr(T* ptr = nullptr);
    ~my_weak_ptr();
    my_weak_ptr(my_weak_ptr& other_ptr);
    my_weak_ptr& operator=(my_weak_ptr& other_ptr);

    T& operator*();
    T* operator->();

private:

    T* m_ptr;
};


template<typename T>
my_weak_ptr<T>::my_weak_ptr(T* ptr):m_ptr(ptr)
{

}

template<typename T>
my_weak_ptr<T>::~my_weak_ptr()
{
    if(m_ptr)
        delete m_ptr;
}

template<typename T>
my_weak_ptr<T>::my_weak_ptr(my_weak_ptr& other_ptr)
{
    this->m_ptr = other_ptr.m_ptr;
}

template<typename T>
my_weak_ptr<T>& my_weak_ptr<T>::operator=(my_weak_ptr& other_ptr)
{
    this->m_ptr = other_ptr.m_ptr;
    return *this;
}

template<typename T>
T&  my_weak_ptr<T>::operator*()
{
    return *this->m_ptr;
}

template<typename T>
T*  my_weak_ptr<T>::operator->()
{
    return this->m_ptr;
}

// A.h
#include"my_weak_ptr.h"

class B;

class A
{
public:
    A();
    ~A();

public:
    my_weak_ptr<B> m_b_ptr;
};


// A.cpp
A::A()
{
    std::cout << "A constructor ---" << std::endl;

}

A::~A()
{
    std::cout << "A destructor ---" << std::endl;
}

// B.h
#include"my_shared_ptr.h"

class A;

class B
{
public:
    B();
    ~B();

public:
    my_shared_ptr<A> m_a_ptr;
};


// B.cpp
#include "b.h"
#include<iostream>
B::B()
{
    std::cout << "B constructor ---" << std::endl;

}

B::~B()
{
    std::cout << "B destructor -- " << std::endl;
}



// main.cpp

void testMyWeakPtr()
{
    my_shared_ptr<A> pa(new A);

    my_weak_ptr<B> pb(new B);

    pa->m_b_ptr = pb;
    pb->m_a_ptr = pa;

}

int main()
{

    testMyWeakPtr();

    return 0;
}

输出:

c++ 学习系列 -- 智能指针,c++,学习,开发语言

忽视其中 B 析构了两次,通过结果可以看到,A 与 B 均能够析构。

 

到了这里,关于c++ 学习系列 -- 智能指针的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • C语言学习系列-->看淡指针(3)

    在指针的类型中我们知道有⼀种指针类型为字符指针 char* 一般使用: 进阶使用 本质是把字符串 hello ⾸字符的地址放到了pstr中。 把⼀个常量字符串的⾸字符 h 的地址存放到指针变量 pstr 中。 经典例题: 运行结果: 这⾥str3和str4指向的是⼀个同⼀个常量字符串。C/C++会把常量

    2024年02月11日
    浏览(21)
  • 【C++】auto_ptr为何被唾弃?以及其他智能指针的学习

    搭配异常可以让异常的代码更简洁 文章目录 智能指针     内存泄漏的危害     1.auto_ptr(非常不建议使用)     2.unique_ptr     3.shared_ptr     4.weak_ptr 总结 C++中为什么会需要智能指针呢?下面我们看一下样例:  在上面的代码中,一旦出现异常那就会造成内存泄漏,什么是内存

    2024年02月11日
    浏览(11)
  • [开发语言][python][c++]:C++中的this指针和Python中的Self -- 26岁生日

    以朋友的新岁祝福开篇,祝笔者也祝大家☺️: 之前一直对 C++ 中的 this 和 python 中的 self 到底是什么关系,为什么 C++ 要显式的写出来,python 则不需要? 模糊不清,趁着周末整理一下相关结论,希望本篇文章可以解答这些问题,同时对C++和Python中的类加深些理解。 python 当

    2024年01月24日
    浏览(28)
  • C/C++|物联网开发入门+项目实战|指针|嵌入式C语言高级|C语言内存空间的使用-学习笔记(9)

    参考: 麦子学院-嵌入式C语言高级-内存空间 内存类型资源地址、门牌号的代名词 指针:地址的代名词 指针变量:存放指针这个概念的盒子 *P char *p *p; C语言娟译器对指针这个特殊的概念,有2个疑问? 1、分配一个盒子,盒子要多大? 在32bit系统中,指针就4个字节 2、盘子里存放

    2023年04月22日
    浏览(20)
  • 【重学C++】02 脱离指针陷阱:深入浅出 C++ 智能指针

    【重学C++】02 脱离指针陷阱:深入浅出 C++ 智能指针 大家好,今天是【重学C++】系列的第二讲,我们来聊聊C++的智能指针。 在上一讲《01 C++如何进行内存资源管理》中,提到了对于堆上的内存资源,需要我们手动分配和释放。管理这些资源是个技术活,一不小心,就会导致内

    2024年02月05日
    浏览(20)
  • C++进阶 智能指针

    本篇博客简介:介绍C++中的智能指针 我们首先来看下面的这段代码 在上面这段代码中有着一个很明显的内存泄露风险 当我们的程序运行在Func函数内的div函数时 很可能因为除0错误而跳转到另外一个执行流从而导致Func函数内两个new出来的内存没法被回收 为了解决这个问题我

    2024年02月13日
    浏览(18)
  • 面试:C++ 11 智能指针

    内存泄露在维基百科中的解释如下: 在计算机科学中,内存泄漏指由于疏忽或错误造成程序未能释放已经不再使用的内存。内存泄漏并非指内存在物理上的消失,而是应用程序分配某段内存后,由于设计错误,导致在释放该段内存之前就失去了对该段内存的控制,从而造成了

    2024年02月07日
    浏览(14)
  • C++——智能指针

    内存泄漏 什么是内存泄漏:内存泄漏指因为疏忽或错误造成程序未能释放已经不再使用的内存的情况。内 存泄漏并不是指内存在物理上的消失,而是应用程序分配某段内存后,因为设计错误,失去了对 该段内存的控制,因而造成了内存的浪费。 内存泄漏的危害:长期运行的

    2024年02月09日
    浏览(39)
  • C++智能指针的发展

    GC–garbage collection垃圾回收,Java里的机制。在头文件 memory 中 堆内存泄漏(Heap leak) 堆内存指的是程序执行中依据须要分配通过malloc / calloc / realloc / new等从堆中分配的一块内存,用完后必须通过调用相应的 free或者delete 删掉。假设程序的设计错误导致这部分内存没有被释放,那

    2023年04月19日
    浏览(13)
  • 【C++】智能指针详解

    今天我们来讲一下c++中的智能指针。 智能指针不是指针,是一个管理指针的类,用来存储指向动态分配对象的指针,负责自动释放动态分配的对象,防止堆内存泄漏。 动态分配的资源,交给一个类对象去管理,当类对象声明周期结束时,自动调用析构函数释放资源 C++ 98 中产

    2024年02月04日
    浏览(29)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包