智能指针/强制类型转换

智能指针

1 为什么 要用智能指针------防止内存泄露

什么是智能指针?

智能指针是一种思想:
RAII :利用对象生命周期来控制程序资源
优点: 1 不需要显式的释放资源
2 随对象生命周期结束释放资源

智能指针分类 :

std::auto_ptr    
std::unique_ptr
std::shared_ptr

1 std::auto_ptr

这个智能指针有它的缺点
当发生对象拷贝 ,或者赋值时,他会悬空前者(即,释放掉了前者的资源)。

#include <iostream>

using namespace std;

class Date
{
  public:
    Date(int year =1900, int month =1, int day =1 )
      :year_(year)
      ,month_(month)
      ,day_(day)
      { }
      

    int year_;
    int month_ ;
    int day_;
};

template <class T>
class Auto_ptr
{
  public:
    Auto_ptr( T* ptr = NULL)
    :ptr_(ptr)
    {}



    //auto_ptr :: 1 拷贝之后就会发生悬空,即被拷贝的对象内容消失,转移到了靠背后的对象中
    Auto_ptr( Auto_ptr<T>& prev)
      :ptr_(prev.ptr_)
    {
      prev.ptr_ = NULL;
    }
    ~Auto_ptr()
    {
      if( ptr_ )
        delete ptr_;
    }

    //auto_ptr  2  被赋值之后发生悬空,前面的对象悬空 ,即 this = tmp ;   tmp 悬空   a  = 2;   原有2中资源悬空
    


    Auto_ptr<T>& operator =(Auto_ptr<T>& tmp) 
    {
      // 检查是否自己给自己赋值
      if(this == &tmp)
      {
        return *this;
      }
      else 
      {
          if(! ptr_)
          {
            delete ptr_;//防止内存泄漏
          }
          
          //转移tmp 的资源到 this 中去
          ptr_ = tmp.ptr_;
          tmp.ptr_ = NULL;   //悬空  赋值者的资源
          return *this;//return *ptr_;
      }
  
    }

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

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


  private:
    T* ptr_;
};


int main()
{
   Auto_ptr<Date> a(new Date);
   Auto_ptr<Date> copy(a);


   //预期 下面两种方式都会发生错误
  // cout<<a->year_<<endl;
    a->year_  = 2018;
   return 0;
}

2std::unique_ptr

unique_ptr 独一无二的指针,
既然独一无二,当然不能进行拷贝,不能赋值拷贝,那么我们将构造函数和 拷贝构造函数都进行私有化,c++11 在函数后边 =delete 私有化

#include <iostream>


using namespace std;

//unique_ptr 独一无二的只能指针,简单粗暴 ,防止拷贝,防止赋值

class Date
{
  public:
    Date(int year , int month, int day)
    :year_(year)
    ,month_(month)
    ,day_(day) 
  {}

  
    int year_;
    int month_;
    int day_;
};

template <class T> 
class Unique_ptr
{

  public:
    Unique_ptr(T* a = NULL)
    :a_(a)
    {}
    
    ~Unique_ptr()
    {
      if(a_)
        delete a_;
    }

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

    T* operator->()
    {
      return a_;
    }
    
  private:
    //防止拷贝 和 赋值 ,我们将其定义为私有成员函数,并且只做声明,不实现
    Unique_ptr(const Unique_ptr<T>& source);
    Unique_ptr& operator = (Unique_ptr<T>& target);
    
    //c++11  新玩法 ,在 函数后面添加  = delete
    T* a_;
};

int main()
{

  return 0;
}

3 std::shared_ptr

shared_ptr 采用引用计数的原理,
类似于,解决前拷贝的原理,当引用计数器大于0 ,即使用拷贝或赋值拷贝引用计数++;
由于++操作是非原子性的,所以要保证线程安全,我们进行加锁。

#include <iostream>
#include <thread>
#include <mutex>
using namespace std;


//共享指针 ,原理 采用 浅拷贝 + 引用计数的思想

template <class T>
class Shared_ptr
{
  public:
    Shared_ptr(T* ptr = nullptr)
    :ptr_(ptr)
    ,pMutex_(new mutex)
    ,pCount_(new int(1))
  {}
    
    ~Shared_ptr()
    {
      Relese();
    }

    Shared_ptr(const Shared_ptr<T>& p)
      :ptr_(p.ptr_)
       ,pMutex_(p.pMutex_)
       ,pCount_(p.pCount_)
    {
          AddpCount();//引用计数
    }
    
    Shared_ptr& operator = (Shared_ptr<T>& sp)
    {
        if(&sp  !=  this )//防止给自己赋值
        {
            delete this->ptr_ ;//先释放自己的资源防止内存泄漏

          //标注资源位置
            this->ptr_ = sp.ptr_;
            this->pCount_ = sp.pCount_;
            this->pMutex_ = sp.pMutex_;

            AddpCount();
        }
        return *this;
    }
    

    int Get()
    {
      return *pCount_;
    }
    
    T& operator *(){return *ptr_; }
    T* operator ->(){return ptr_; }



  private:
  void AddpCount()
 {
   pMutex_->lock();
   ++(*pCount_);//这里的引用计数参数必须是指针类型,  因为我们只保留一份数据,传值的话就保留了多份数据,不符合引用计数的思想
   pMutex_->unlock();
 }


  void Relese()
 {
   bool delete_falg = false;
   pMutex_->lock();
   --*pCount_;
   if(*pCount_ == 0)
     delete_falg = true;

   pMutex_->unlock();
  
  if(delete_falg == true)
  {
      delete pCount_;
      delete pMutex_;
      delete ptr_;
  }
 }

    T* ptr_;
    mutex* pMutex_;
    //这里的引用计数参数必须是指针类型,  因为我们只保留一份数据,传值的话就保留了多份对象,不符合引用计数的思想
    int* pCount_;
};

std::shared_ptr 的循环引用问题:

struct ListNode
{
//什么类型的对象,就需要什么类型的节点类型
shared_ptr<ListNode> prev_;//智能指针
shared_ptr<ListNode> next_;//智能指针
}//即 一个ListNode 具有两个智能指针,
int  main()
{
	shared_ptr<ListNode> node1(new ListNode)
	shared_ptr<ListNode> node2(new ListNode)
	node1 ->next_ = node2;
	node2 ->prev_ = node1;
return 0;
}
//注意 ;node1 想要释放,就需要node2不使用node1


node2 想释放,就需要node1 不使用,
这样形成了循环引用,

导致 node1 中的引用计数一直是2;
node2 中的引用计数一直是2;

解决方案 ,我们将节点内的指针的类型改为weak_ptr;

注意weak_ptr 是一个专门辅助shard_ptr的;它不会增加引用计数:

struct ListNode
{
//weak_ptr  不会增加引用计数,专门辅助shared_ptr
weak_ptr<ListNode> prev_;//智能指针
weak_ptr<ListNode> next_;//智能指针
}//即 一个ListNode 具有两个智能指针,
int  main()
{
	shared_ptr<ListNode> node1(new ListNode)
	shared_ptr<ListNode> node2(new ListNode)
	node1 ->next_ = node2;
	node2 ->prev_ = node1;
return 0;
}

C++ 强制类型转换:

1 为什么C++又有自己的强制类型转换
c中的类型转换可视性差,不容易快速定位错误位置,c++为了类型转换的可视性,c++增加了4种强制类型转换。

1 static_cast
static_cast 用于静态类型转换(不能用于多态等),并且,相互转换的类型得有所联系。

2 reinterpret_cast 为操作数的位模式提供低层次的重新解释
reinterpret_cast用于不同类型之间的强制转换。
3 const_cast 用于修改const类型变量。

4 dynamic_cast
1 dynamic_cast 只能同于含有虚函数的类
2 dynamic_cast 会进行一个预检查,可以成功,则进行转换,不可以,返回0。

explicit :防止构造函数隐式类型转换

	eg: Date a = 6;
	内部其实 :  Date tmp(6);
						Date a(tmp) ;

最后:我们应该尽量避免使用C++的强制类型转换,强制类型转换,会自动挂起或者关闭编译器的正常的类型检查 在强制类型出错的地方不会发出警告

发布了90 篇原创文章 · 获赞 13 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/weixin_44030580/article/details/104542270