IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> C++知识库 -> auto_ptr、unique_ptr、shader_ptr的实现及简单了解 -> 正文阅读

[C++知识库]auto_ptr、unique_ptr、shader_ptr的实现及简单了解

为什么要使用智能指针:

智能指针的作用是管理一个指针,因为申请的空间在函数结束时忘记了释放,就造成了内存泄漏。使用智能指针可以很大程度上避免这个问题,因为智能指针就是一个类,当超出类的作用范围就会调用析构函数,析构函数就会自动释放内存资源。所以智能指针的作用就是在函数结束时自动释放内存空间,不需要手动释放;

四种智能指针:

auto_ptr 采用所有权模式

智能指针的最基本功能:对超出作用域的对象进行释放。

#include <iostream>

struct Arwen
{
  void Test()
  {
    std::cout << "i am arwen" << std::endl;
  }
};

template< class T>
class my_auto_ptr
{
private:

  T* m_ptr; //被封装的指针

public:

  my_auto_ptr(T* p) :m_ptr(p) { }

  ~my_auto_ptr() { delete m_ptr; }

  T& operator*() { return *m_ptr; }

  T* operator->() { return m_ptr; }
};

int main()
{
  my_auto_ptr<int> mp1(new int(88));   //等价int* ip = new int(88);

  int num = *mp1;   //等价int num = *ip;

  my_auto_ptr<Arwen> mp2(new Arwen);   //等价Arwen* ip = new Arwen;
  mp2->Test();
  return 0;
}

上面这个类就是一个特别基础的智能指针;

之后我们在完善一下给他加上转移所有权功能:

#include <iostream>

template< class T >
class my_auto_ptr 
{
public:
  explicit my_auto_ptr(T* ptr = nullptr) : ptr_(ptr) { }
  
  ~my_auto_ptr() 
  {
    delete ptr_;
  }

  T* getPtr() const 
  {
    return ptr_;
  }

  my_auto_ptr(my_auto_ptr& mp)
  {
    ptr_ = mp.release();
  }

  my_auto_ptr& operator= (const my_auto_ptr& ap)
  {
    my_auto_ptr(ap).swap(*this);
    return *this;
  }

  T* release()
  {
    T* ptr = ptr_;
    ptr_ = nullptr;
    return ptr;
  }
  
  void swap(my_auto_ptr& ap)
  {
    using std::swap;
    swap(ptr_, ap.ptr_);
  }
  
  T& operator*() const { return *ptr_; }

  T* operator->() const { return ptr_; }
  
  operator bool() const { return ptr_; }

private:
  T* ptr_;
};

struct Arwen 
{
  int age;

  Arwen(int gg) :age(gg) { };
};

int main()
{
  my_auto_ptr<Arwen> myPtr(new Arwen(24));
  int num = myPtr->age; //正确
  std::cout << num << std::endl;

  my_auto_ptr<Arwen> ptrOne(myPtr);  //复制构造
  //num =myPtr->age; //该处会出错.因为把myPtr复制给ptrOne后,它自己本身相当于失效了
  std::cout << num << std::endl;

  num = ptrOne->age; //正确
  std::cout << num << std::endl;
  
  my_auto_ptr<Arwen> ptrTwo = ptrOne;
  Arwen* pArwen = new Arwen(88);
  //ptrTwo.release(); 释放所有权
  //num = ptrTwo->age;
  //std::cout << num << std::endl;

  num = pArwen->age; //此处的值是88了,而不是以前的24
  std::cout << num << std::endl;

  return 0;
}

unique_ptr 独占所指向的对象:

上面的auto_ptr如果不小心将对象传递给了另一个对象它就不再拥有这个对象了;所以为了解决这种问题提出了unique_ptr这种独占式的智能指针;

也就是一个对象只能被一个指针拥有;

实现代码如下:

#include <iostream>
#include <utility>

template< class T >
class my_unique_ptr
{
public:
  explicit my_unique_ptr(T* ptr = nullptr) : ptr_(ptr) { }

  ~my_unique_ptr()
  {
    delete ptr_;
  }

  T* getPtr() const
  {
    return ptr_;
  }
  
  /*
  	理论上,这里的模板参数smart_ptr<U<&&是万能引用,既可以引用左值,又可以引用右值,万能引用在【完美转发】中大有用武之地。 因此上面这段代码所表达的是一个构造函数模板,实例化后可能是拷贝构造函数,也可能是移动构造函数。 
  */
  template <typename U>
  my_unique_ptr(my_unique_ptr<U>&& mp) //将其改为移动构造函数
  {
    ptr_ = mp.release();
  }

  /*operator=()的参数在接收参数的时候,会调用构造函数,
    如果调用的是拷贝构造,那赋值操作就是拷贝,如果调用的是移动构造,那么赋值操作就是移动。
  */
  my_unique_ptr& operator= (my_unique_ptr ap)
  {
    ap.swap(*this);
    return *this;
  }

  T* release()
  {
    T* ptr = ptr_;
    ptr_ = nullptr;
    return ptr;
  }

  void swap(my_unique_ptr& ap)
  {
    using std::swap;
    swap(ptr_, ap.ptr_);
  }

  T& operator*() const { return *ptr_; }

  T* operator->() const { return ptr_; }

  operator bool() const { return ptr_; }

private:

  T* ptr_;
};

struct Arwen
{

  int age;

  Arwen(int gg) :age(gg) { };

};

int main()
{

  my_unique_ptr<Arwen> ptr1(new Arwen(11));
  //my_unique_ptr<Arwen> ptr2(ptr1);  //错误
  std::cout << ptr1->age << std::endl;

  my_unique_ptr<Arwen> ptr3(new Arwen(33));
  std::cout << ptr3->age << std::endl;
  //ptr3 = ptr1;  //错误
  ptr3 = std::move(ptr1); //可以
  std::cout << ptr3->age << std::endl;

  my_unique_ptr<Arwen>ptr4{ std::move(ptr3) };  //可以
  std::cout << ptr4->age << std::endl;

  return 0;
}

极客时间评论: 在C++11中,标准库在中提供,std::move并不能移动任何东西,它唯一的功能是将一个左值强制转化为右值引用,继而可以通过右值引用使用该值,以用于移动语义。从实现上讲,std::move基本等同于一个类型转换:static_cast<T&&>(lvalue);

对于万能引用和完美转发有兴趣的可以看 https://blog.csdn.net/theonegis/article/details/86568427

shader_ptr 允许多个指针指向同一个对象

shader_ptr采用的是引用计数的形式,可以多个智能指针指向同一个对象,还共享同一个引用计数,该对象会在最后一个引用被销毁是释放。
在这里插入图片描述
shared_ptr的一些基本操作:

std::shared_ptr<int> p1; 
std::shared_ptr<int> p1(new int(1));	//构造函数初始化
std::shared_ptr<int> p2 = p1;
cout << "p2 use_count:" << p2.use_count() << endl;	//获取引用计数
get();	//获取原始指针

shared_ptr实现:

#include <iostream>
#include <utility>  // std::swap

//计数类
class shared_count
{
public:
  shared_count() : count(1) { }

  //增加计数
  void add_count()
  {
    ++count;
  }

  //减少计数
  long reduce_count()
  {
    return --count;
  }

  //获取引用计数
  long get_count() const
  {
    return count;
  }

private:
  //用于存储引用计数
  long count;
};

template <typename T>
class my_shared_ptr 
{
public:
  template <typename U>
  friend class my_shared_ptr;

  explicit my_shared_ptr(T* ptr = nullptr) : ptr_(ptr)
  {
    if (ptr) 
    {
      shared_count_ = new shared_count();
    }
  }
 
  //指向的对象和引用计数都为非空,计数减一,并在引用计数彻底降为零时彻底释放对象和引用计数;
  ~my_shared_ptr()
  {
    if (ptr_ && ! shared_count_->reduce_count()) 
    {
      delete ptr_;
      delete shared_count_;
    }
  }

  my_shared_ptr(const my_shared_ptr& other)
  {
    ptr_ = other.ptr_;
    if (ptr_) 
    {
      other.shared_count_->add_count();
      shared_count_ = other.shared_count_;
    }
  }

  template <typename U>
  my_shared_ptr(const my_shared_ptr<U>& other) noexcept
  {
    ptr_ = other.ptr_;
    if (ptr_) 
    {
      other.shared_count_->add_count();
      shared_count_ = other.shared_count_;
    }
  }

  template <typename U>
  my_shared_ptr(const my_shared_ptr<U>& other, T* ptr) noexcept
  {
    ptr_ = ptr;
    if (ptr_) 
    {
      other.shared_count_->add_count();
      shared_count_ = other.shared_count_;
    }
  }

  my_shared_ptr& operator=(my_shared_ptr rhs) noexcept
  {
    rhs.swap(*this);
    return *this;
  }

  T* get() const noexcept
  {
    return ptr_;
  }

  long use_count() const noexcept
  {
    if (ptr_) 
    {
      return shared_count_->get_count();
    }
    else 
    {
      return 0;
    }
  }
  void swap(my_shared_ptr& rhs) noexcept
  {
    using std::swap;
    swap(ptr_, rhs.ptr_);
    swap(shared_count_,rhs.shared_count_);
  }

  T& operator*() const noexcept
  {
    return *ptr_;
  }
  T* operator->() const noexcept
  {
    return ptr_;
  }
  operator bool() const noexcept
  {
    return ptr_;
  }

private:
  T* ptr_;
  shared_count* shared_count_;
};


class shape 
{
public:
  virtual ~shape() {}
};

class circle : public shape {
public:
  ~circle() { puts("~circle()"); }
};

int main()
{
  my_shared_ptr<circle> ptr1(new circle());
  printf("use count of ptr1 is %ld\n", ptr1.use_count());

  my_shared_ptr<shape> ptr2;
  printf("use count of ptr2 was %ld\n", ptr2.use_count());

  ptr2 = ptr1;
  printf("use count of ptr2 is now %ld\n", ptr2.use_count());
  if (ptr1) 
  {
    puts("ptr1 is not empty");
  }
}

weak_ptr 伴随类

weak_ptr它是一种弱引用,指向shader_ptr所管理的对象,只要用于解决shader_ptr互相引用导致的死锁问题。
weak_ptr是用来解决shared_ptr相互引用时的死锁问题,如果说两个shared_ptr相互引用,那么这两个指针的引用计数永远不可能下降为0,资源永远不可能释放。Weak_ptr它是对对象的一种弱引用,他的构造和析构不会增加对象的引用计数;

总结:

以上的实现都只是简单实现并非考虑到了全部的情况;
以上代码皆参考与链接2 《现代c++实战30讲》;

参考:

《c++ primer》

https://blog.csdn.net/pzhw520hchy/article/details/77920914?locationNum=6&fps=1

https://time.geekbang.org/column/article/169263

  C++知识库 最新文章
【C++】友元、嵌套类、异常、RTTI、类型转换
通讯录的思路与实现(C语言)
C++PrimerPlus 第七章 函数-C++的编程模块(
Problem C: 算法9-9~9-12:平衡二叉树的基本
MSVC C++ UTF-8编程
C++进阶 多态原理
简单string类c++实现
我的年度总结
【C语言】以深厚地基筑伟岸高楼-基础篇(六
c语言常见错误合集
上一篇文章      下一篇文章      查看所有文章
加:2022-03-06 12:43:52  更:2022-03-06 12:45:29 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/24 4:41:03-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码