解释:智能指针是针对内存泄漏和多次释放同一块内存空间而引发问题的一种解决方式
唯一指针:不能通过拷贝构造、赋值构造实现指针对象A赋值给指针对象2,所以可以只能由唯一指针指向内存空间。当然也可以通过移动构造、移动赋值,实现对象A赋值给对象2,移动成功后,指针对象A失效。
//参考代码
#include <iostream>
using namespace std;
#include <memory>
class Rectangle {
int length;
int breadth;
public:
Rectangle(int l, int b){
length = l;
breadth = b;
}
int area(){
return length * breadth;
}
};
int main(){
unique_ptr<Rectangle> P1(new Rectangle(10, 5));
cout << P1->area() << endl; // This'll print 50
unique_ptr<Rectangle> P2;
// unique_ptr<Rectangle> P2(P1); // 无法拷贝构造
// P2 = P1; // 无法赋值构造
P2 = move(P1);
cout << P2->area() << endl;
// cout<<P1->area()<<endl; // 已经传递,P1 无所有权
return 0;
}
// Disable copy from lvalue.不允许复制,体现专属所有权语义
unique_ptr(const unique_ptr&) = delete;
unique_ptr& operator=(const unique_ptr&) = delete;
// Move constructor.体现专属所有权语义和只移型别
// 只允许使用移动拷贝构造函数
// 如果复制一个unique_ptr对象,会将源unique_ptr对象管理的资源release掉
unique_ptr(unique_ptr&& __u) noexcept
: _M_t(__u.release(), std::forward<deleter_type>(__u.get_deleter())) { }
// 这个也是移动拷贝构造函数
// 只是使用的类型是可以隐式转换的其他unique_ptr对象
template<typename _Up, typename _Ep, typename = _Require<
__safe_conversion_up<_Up, _Ep>,
typename conditional<is_reference<_Dp>::value,
is_same<_Ep, _Dp>,
is_convertible<_Ep, _Dp>>::type>>
unique_ptr(unique_ptr<_Up, _Ep>&& __u) noexcept
: _M_t(__u.release(), std::forward<_Ep>(__u.get_deleter()))
{ }
// Assignment,也可以说明是专属所有权语义和只移型别
unique_ptr& operator=(unique_ptr&& __u) noexcept
{
// __u.release()释放并返回源unique_ptr对象管理的资源
// reset是将__u.release()返回的资源赋给目标(当前)unique_ptr对象
reset(__u.release());
get_deleter() = std::forward<deleter_type>(__u.get_deleter());
return *this;
}
// 析构函数,调用析构器析构掉管理的资源,并将__ptr指向nullptr
~unique_ptr()
{
auto& __ptr = _M_t._M_ptr();
if (__ptr != nullptr)
get_deleter()(__ptr);
__ptr = pointer();
}
强指针和弱指针 带引用计数的指针
多个指针指向同一资源 给每一资源匹配一个引用计数,当智能使用资源,引用计数就加一,当智能指针释放资源时,引用计数就减一,当引用计数为0时,就释放资源。
#include <iostream>
#include <memory>
#include <stdio.h>
using namespace std;
//对资源进行引用计数的类
template <typename T>
class RefCnt
{
private:
/* data */
T* mptr; //指向
int mcount; //atomic_int 原子整形类通过CAS保证
//RefCnt<T>* mpRefCnt;
public:
RefCnt(T* ptr = nullptr):mptr(ptr)
{
if(mptr != nullptr)
mcount = 1;
}
void addRef()
{
mcount++; //添加资源引用计数
}
int delRef()
{
return --mcount;
}
~RefCnt(){};
};
template <typename T>
class CSmartPtr{ //shared_ptr 、weak_ptr都可以在多线程环境下使用()线程安全满足
//在多线程环境下并不安全
// ++ -- 并不是一个线程安全的操作
private:
T* mptr; //指向资源的指针
RefCnt<T>* mpRefCnt; //指向该资源引用计数对象的指针
public:
CSmartPtr(T* ptr = nullptr):mptr(ptr)
{
//建立一个引用计数对象 对着施磊视频敲,在这块卡着动不了,他的代码是 mpRefCnt = new RefCnt(mptr);
mpRefCnt = new RefCnt<T> (mptr);
}
~CSmartPtr()
{
if(0 == mpRefCnt->delRef()){
delete mptr;
mptr = nullptr;
}
}
//return by reference 传递着无须知道接收者以reference形式接收
T& operator*(){ return *mptr;}
T* operator->(){ return mptr;}
//重写拷贝构造函数 指向资源的智能指针对象 指向引用计数类的指针对象
CSmartPtr(const CSmartPtr<T>& src):mptr(src.mptr), mpRefCnt(src.mpRefCnt)
{
//有资源
if(mptr != nullptr){
mpRefCnt->addRef();
}
}
//赋值重载
CSmartPtr<T>& operator= (const CSmartPtr<T> &src)
{
if(this == &src)
return *this;
//this对象本身引用计数要减一
if(0 == mpRefCnt->delRef())
{
delete mptr;
mptr = nullptr;
}
//给资源添加引用计数
//防止多个智能指针 引用同一个资源,进行多次释放
mptr = src.mptr;
mpRefCnt = src.mpRefCnt;
mpRefCnt->addRef();
return *this;
}
};
int main()
{
CSmartPtr<int> ptr1(new int);
CSmartPtr<int> ptr2(ptr1);
CSmartPtr<int> ptr3 = ptr2;
*ptr1 = 20;
cout<<*ptr2<<" "<<*ptr3<<endl; //20 20
return 0;
}
强弱智能指针使用场景
C++ muduo库
多线程访问共享对象的线程安全问题
主线程在作用域内创建子线程,然后子线程通过智能指针调用方法,当出作用域后,应该释放自能指针空间的,子线程还在使用智能指针调用方法。这是有问题的。
弱指针没有封装解引用、键指向重载,所以不能对引用计数发生改变,也就不会使用共享资源。
强指针相互调用会出现最后资源无法释放(?)
```bash
智能指针
带引用计数 (带引用计数的好处,多个指针可以管理同一个资源,联想信号量机制,给资源添加一个计数器,当使用资源的时候,也就是有智能指针在调用,引用计数加一,当指针停止使用,引用计数减一,然后判断减一后的状态,如果引用计数不为0,则说明还有指针在使用这个资源,不进行析构处理,当引用计数为0,进行析构处理)shareed_ptr(会改变指针的引用计数)、weak_ptr(不会改变指针的引用计数)(weak_ptr 观察强指针shared_ptr,然后强指针观察资源)(强指针循环引用造成的问题(举例,在两个A、B分别添加创建对方的智能对象,然后在使用的时候,定义两个实例的智能智能,然后让这两个对象分别指向对方的对象,最后就会实现两个类的引用计数都加了1(类中的对方的对象指向栈中定义的对象,引用计数会加1),也就最后都是2,但是呢,后面没有释放资源。解决方案:定义的时候强智能指针,引用的时候用弱智能指针(在类中定义的指针为弱智能指针)所以当使用pa->_ptrb = pb 这样,需要引用B类型的智能指针,就不会把B类型的指针指针的引用计数加一)
不带引用计数 auto_ptr、scope_ptr、unique_ptr (浅拷贝 ==》指针对象析构两次) 解决浅拷贝问题(auto_ptr 通过从新定义一个新指针把当前的指针指向它,然后把当前的指针置空,接着把ptr2 指向上面的新指针,所以最后最开始的指针成了空指针,两个指针只有一个指针有用)(scope_ptr则是直接把拷贝构造和赋值构造给delete)(unique_ptr 删除了拷贝构造、赋值构造,但是新增了右值引用的拷贝构造、和右值引用的赋值构造)
智能指针存在的问题:交叉引用(循环引用)问题
多线程访问共享对象问题
自定义删除器
std::move 获得当前变量的右值类型
```
[参考---智能指针](浅谈C++的智能指针 · Salad Days