首页 > 编程知识 正文

指针类型的强制转换,智能指针定义

时间:2023-05-06 12:13:04 阅读:220541 作者:671

智能指针

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

什么是智能指针?

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

智能指针分类 :

std::auto_ptr std::unique_ptrstd::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_ptrweak_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++的强制类型转换,强制类型转换,会自动挂起或者关闭编译器的正常的类型检查 在强制类型出错的地方不会发出警告

版权声明:该文观点仅代表作者本人。处理文章:请发送邮件至 三1五14八八95#扣扣.com 举报,一经查实,本站将立刻删除。