@[TOC]
问题
对于动态开辟的空间,虽然可以通过异常来规避某些内存泄漏的可能,但是这种通过异常的方式太过于麻烦,而面向对象语言有个特性:自动获取资源,生命周期结束自动释放资源。----也就是RAII思想
那么对于动态开辟指针我们也可以基于RAII思想,包装这个指针----也就是智能指针
RAII
RAII(Resource Acquisition Is Initialization)是一种利用对象生命周期来控制程序资源(如内存、文件句柄、网络连接、互斥量等等)的简单技术。
**在对象构造时获取资源,接着控制对资源的访问使之在对象的生命周期内始终保持有效,最后在对象析构的时候释放资源。**
借此,我们实际上把管理一份资源的责任托管给了一个对象。这种做法有两大好处:
- 不需要显式地释放资源。
- 采用这种方式,对象所需的资源在其生命期内始终保持有效
c++98 智能指针
std::auto_ptr
管理权转移,一但出现指针赋值或者拷贝,将指针的内容进行转换,同时赋值为null的行为
c++11智能指针
std::unique_ptr
这款的智能指针不允许拷贝,也不允许赋值
//唯一智能指针
template<class T, class D>
class unique_ptr
{
typedef unique_ptr<T, D> Self;
typedef T& Ref;
typedef T* Ptr;
public:
unique_ptr(T* ptr)
:_ptr(ptr)
{
}
//c++98实现un_ique
//1. 将拷贝构造函数和拷贝赋值重载 只声明不实现,但是不法杜绝外部显示定义
//2. 声明在private区域
//c++11实现un_ique
//通过关键字 强制不生成拷贝构造函数和拷贝赋值重载
unique_ptr(const Self& uq) = delete;//强制不生成拷贝构造函数
Self& operator=(const Self& uq) = delete;//强制不生成拷贝赋值重载
~unique_ptr()
{
D()(_ptr);
_ptr = nullptr;
}
private:
Ptr _ptr;
};
std::shared_ptr
这款指针指针允许正常指针的任何操作
//支持计数和拷贝,赋值等
template<class T>
class Shared_ptr
{
typedef Shared_ptr<T> Self;
typedef T& Ref;
typedef T* Ptr;
public:
template<class T>
friend class Weak_ptr;
void Release()//减计数
{
if ( _pcnt&&--(*_pcnt)==0 && _ptr)
{
std::cout << "delete" << _ptr << " " << "~shard_ptr()" << std::endl;
delete _ptr;
_ptr = nullptr;
delete _pcnt;
_pcnt = nullptr;
}
}
void Destory()//直接销毁
{
if (_pcnt && *_pcnt >= 1 && _ptr)
{
std::cout << "delete" << _ptr << " " << "~shard_ptr()" << std::endl;
delete _ptr;
_ptr = nullptr;
delete _pcnt;
_pcnt = nullptr;
}
}
Shared_ptr() = default;
Shared_ptr(Ptr ptr)
:_ptr(ptr),
_pcnt(new int(1))
{
}
Shared_ptr(const Self& sp)
:_ptr(nullptr),
_pcnt(nullptr)
{
_ptr = sp._ptr;
_pcnt = &(++(*sp._pcnt));
}
Shared_ptr( const Weak_ptr<T>& wp)
:_ptr(nullptr),
_pcnt(nullptr)
{
_ptr = wp._ptr;
_pcnt = &(++(*wp._pcnt));
}
Self& operator=(const Self& sp)
{
//比较this指针的缺陷:不同对象的_ptr可以相同
if (_ptr!=sp._ptr)
{
Release();
_ptr = sp._ptr;
_pcnt = &(++(*sp._pcnt));
}
return *this;
}
Self& operator=( const Weak_ptr<T>& wp)
{
//比较this指针的缺陷:不同对象的_ptr可以相同
if (_ptr != wp._ptr)
{
Release();
_ptr = wp._ptr;
_pcnt = &(++(*wp._pcnt));
}
return *this;
}
Ref operator*()
{
return *_ptr;
}
Ptr operator->()
{
return &*_ptr;
}
bool operator==(const Self& sp)
{
return _ptr == sp._ptr;
}
bool operator!=(const Self& sp)
{
return _ptr != sp._ptr;
}
bool operator==(const Weak_ptr<T>& wp)
{
return _ptr == wp._ptr;
}
bool operator!=(const Weak_ptr<T>& wp)
{
return _ptr != wp._ptr;
}
~Shared_ptr()
{
Release();
}
private:
Ptr _ptr = nullptr;
int* _pcnt = nullptr;
};
std::weak_ptr
除了不参与内存的释放,其它与shared_ptr没有区别
//弱指针,不参与内存的释放,也不参与计数
//默认的析构函数就可以完成不参与内存的释放
template<class T>
class Weak_ptr
{
typedef Weak_ptr<T> Self;
typedef T& Ref;
typedef T* Ptr;
public:
template<class T>
friend class Shared_ptr;
Weak_ptr() = default;
Weak_ptr(Ptr ptr)
:_ptr(ptr),
_pcnt(new int(1))
{
};
void Release()//减计数
{
if (--(*_pcnt) == 0 && _ptr)
{
std::cout << "delete" << _ptr << " " << "~shard_ptr()" << std::endl;
delete _ptr;
_ptr = nullptr;
delete _pcnt;
_pcnt = nullptr;
}
}
void Destory()//直接销毁
{
if (_pcnt && *_pcnt >= 1 && _ptr)
{
std::cout << "delete" << _ptr << " " << "~shard_ptr()" << std::endl;
delete _ptr;
_ptr = nullptr;
delete _pcnt;
_pcnt = nullptr;
}
}
Weak_ptr(const My_SP::Shared_ptr<T>& sp)
{
try
{
if (expired(sp))
{
throw "对象已过期";
}
}
catch (const char*str)
{
std::cout << __LINE__ <<" " << str << std::endl;
}
_ptr = sp._ptr;
_pcnt = sp._pcnt;
}
Weak_ptr(const Self& wp)
{
try
{
if (expired(wp))
{
throw "对象已过期";
}
}
catch (const char* str)
{
std::cout << __LINE__ << " " << str << std::endl;
}
_ptr = wp._ptr;
_pcnt = wp._pcnt;
}
Self& operator=(const Self& sp)
{
//比较this指针的缺陷:不同对象的_ptr可以相同
if (_ptr != sp._ptr)
{
_ptr = sp._ptr;
_pcnt = sp._pcnt;
}
return *this;
}
//检测是否过期
bool expired(const My_SP::Shared_ptr<T>& sp)
{
return !(*(sp._pcnt) >= 1 && sp._ptr);
}
bool expired(const Self& wp)
{
return !(wp._ptr);
}
bool operator==(const Self& wp)
{
return _ptr == wp._ptr;
}
bool operator!=(const Self& wp)
{
return _ptr != wp._ptr;
}
bool operator==(const My_SP::Shared_ptr<T>& sp)
{
return _ptr == sp._ptr;
}
bool operator!=(const My_SP::Shared_ptr<T>& sp)
{
return _ptr != sp._ptr;
}
Ref operator*()
{
return *_ptr;
}
Ptr operator->()
{
return _ptr;
}
private:
Ptr _ptr=nullptr;
int* _pcnt=nullptr;
};
为什么存在weak_ptr
shared_ptr在某些环境会出现循环引用问题
观察下列代码
template<class T>
class Node
{
Node(const T& data = T())
:_data(data),
{
;
}
T _data;
My_SP::Shared_ptr<int> _next;
My_SP::Shared_ptr<int> _prev;
};
void T()
{
My_SP::Shared_ptr<Node<int>> A;
My_SP::Shared_ptr<Node<int>> B;
A->_next = B;
B->_prev =A;
}
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-y2eKxeNE-1665135358975)(./%E6%99%BA%E8%83%BD%E6%8C%87%E9%92%88.assets/image-20221007160921468-1665130162683-1.png)]
template<class T>
class Weak_ptr
{
typedef Weak_ptr<T> Self;
typedef T& Ref;
typedef T* Ptr;
public:
template<class T>
friend class Shared_ptr;
Weak_ptr(Ptr ptr)
:_ptr(ptr),
_pcnt(new int(1))
{
//这里一定要初始化_pcnt的指向对象值为1,
//有需求
};
void Release()//减计数
{
if (--(*_pcnt) == 0 && _ptr)
{
delete _ptr;
_ptr = nullptr;
delete _pcnt;
_pcnt = nullptr;
std::cout << "delete" << _ptr << " " << "~shard_ptr()" << std::endl;
}
}
Weak_ptr(const My_SP::Shared_ptr<T>& sp)
{
try
{
if (expired(sp))
{
throw "对象已过期";
}
}
catch (const char*str)
{
std::cout << __LINE__ <<" " << str << std::endl;
}
_ptr = sp._ptr;
_pcnt = sp._pcnt;
}
Weak_ptr(const Self& wp)
{
try
{
if (expired(wp))
{
throw "对象已过期";
}
}
catch (const char* str)
{
std::cout << __LINE__ << " " << str << std::endl;
}
_ptr = wp._ptr;
_pcnt = wp._pcnt;
}
Self& operator=(const Self& sp)
{
//比较this指针的缺陷:不同对象的_ptr可以相同
if (_ptr != sp._ptr)
{
_ptr = sp._ptr;
_pcnt = sp._pcnt;
}
return *this;
}
//检测是否过期
bool expired(const My_SP::Shared_ptr<T>& sp)
{
return !(*(sp._pcnt) >= 1 && sp._ptr);
}
bool expired(const Self& wp)
{
return !(wp._ptr);
}
bool operator==(const Self& wp)
{
return _ptr == wp._ptr;
}
bool operator!=(const Self& wp)
{
return _ptr != wp._ptr;
}
bool operator==(const My_SP::Shared_ptr<T>& sp)
{
return _ptr == sp._ptr;
}
bool operator!=(const My_SP::Shared_ptr<T>& sp)
{
return _ptr != sp._ptr;
}
Ref operator*()
{
return *_ptr;
}
Ptr operator->()
{
return _ptr;
}
private:
Ptr _ptr=nullptr;
int* _pcnt=nullptr;
};
指针指针简化版的list
- Node内部使用weak_ptr来避免死循环释放问题
- 为了避免节点被局部变量无意间释放,使用weak_ptr来避免出现问题
- 对于节点的释放可以通过weak_ptr直接使用Destory函数销毁而不是减计数
- 在需要使用智能指针的地方,像list这种,要优先考虑weak_ptr
#pragma once
#include "SmartPtr.h"
#include <assert.h>
namespace My_list
{
template<class T>
struct listNode
{
listNode(const T data = T())
:_data(data)
{
}
listNode(const listNode<T>& node)
{
_data = node._data;
_next = node._next;
_prev = node._prev;
}
T _data;
My_SP::Weak_ptr<listNode<T>> _next ;
My_SP::Weak_ptr<listNode<T>> _prev ;
};
template<class T>
class list
{
typedef listNode<T> Node;
typedef list<T> Self;
public:
list()
:_head(new Node),
_sz(0)
{
_head->_prev = _head;
_head->_next = _head;
}
list(const Self& lt)
:_head(lt._head),
_sz(lt.sz)
{
}
void Push(const T &data)
{
My_SP::Weak_ptr<Node> newnode = new Node(data);
//必须要用Weak_ptr,
My_SP::Weak_ptr<Node> tmp = _head->_prev;
tmp->_next = newnode;
newnode->_prev = tmp;
newnode->_next = _head;
_head->_prev = newnode;
++_sz;
}
void erase(My_SP::Weak_ptr<Node> pos)
{
if (_sz == 0)
{
}
else
{
My_SP::Weak_ptr<Node> prevnode = pos->_prev;
My_SP::Weak_ptr<Node> nextnode = pos->_next;
prevnode->_next = nextnode;
nextnode->_prev = prevnode;
--_sz;
}
pos.Destory();
}
bool empty()
{
return _sz == 0;
}
void Destory(My_SP::Weak_ptr<Node> head)
{
if (head->_next == _head)
{
erase(head);
return;
}
Destory(head->_next);
erase(head);
}
~list()
{
Destory(_head);
}
void Print()
{
My_SP::Weak_ptr<Node> pos = _head->_next;
while (pos != _head)
{
cout << pos->_data << endl;
pos = pos->_next;
}
cout << endl;
}
private:
My_SP::Shared_ptr<Node> _head;
size_t _sz = 0;
};
}
类型转换
c语言中的类型转换
在C语言中,如果赋值运算符左右两侧类型不同,或者形参与实参类型不匹配,或者返回值类型与接收返回值类型不一致时,就需要发生类型转化,C语言中总共有两种形式的类型转换:隐式类型
转换和显式类型转换。
- 隐式类型转化:编译器在编译阶段自动进行,能转就转,不能转就编译失败
- 显式类型转化:需要用户自己处理
缺陷:
- 转换的可视性比较差,所有的转换形式都是以一种相同形式书写,难以跟踪错误的转换
- 隐式类型转化有些情况下可能会出问题:比如数据精度丢失
- 显式类型转换将所有情况混合在一起,代码不够清晰
C++11类型转换
static_cast
static_cast用于非多态类型的转换(静态转换),编译器隐式执行的任何类型转换都可用static_cast,但它不能用于两个不相关的类型进行转换:2种数据类型是不同的如:表示数据大小的和表示人的数据等
reinterpret_cast
reinterpret_cast操作符通常为操作数的位模式提供较低层次的重新解释,用于将一种类型转换为另一种不同的类型 ,但是转换不改变之前的属性即const还是const,非const还是非const
const_cast
const_cast最常用的用途就是删除变量的const属性,方便赋值
dynamic_cast
用于基类与派生类间的转换,这种转换有2种:向上和向下转换
向上转型:子类对象指针/引用->父类指针/引用(不需要转换,赋值兼容规则)
向下转型:父类对象指针/引用->子类指针/引用(用dynamic_cast转型是安全的)
注意:
- dynamic_cast只能用于父类含有虚函数的类
- dynamic_cast会先检查是否能转换成功,能成功则转换,不能则返回0
为什么存在向下转换?当给定的对象是一个基类的指针或者引用时,我们无法确定指向的是基类还派生类,这种可以通过dynamic_cast进行区分。
当基类指针指向基类时,向派生类转换时dynamic_cast会转换失败,返回nullptr
当基类指针指向派生时,向派生类转换时dynamic_cast会转换成功。
class A
{
public :
virtual void f(){}
};
class B : public A
{};
void fun (A* pa)
{
// dynamic_cast会先检查是否能转换成功,能成功则转换,不能则返回
B* pb1 = static_cast<B*>(pa);
B* pb2 = dynamic_cast<B*>(pa);
cout<<"pb1:" <<pb1<< endl;
cout<<"pb2:" <<pb2<< endl;
}