** 什么是智能指针,为什么要有智能指针?**
- 智能指针是一个类,它封装了一个原始的C++指针,以管理所指对象的生命期。没有单一的智能指针类型,但所有这些都尝试以实用的方式抽象原始指针。
智能指针应优先于原始指针。 如果你觉得你需要使用指针(首先要考虑你是否真的需要指针),你通常会想要使用智能指针,因为这可以缓解原始指针的许多问题,主要是忘记删除对象和泄漏内存。
如果使用原始指针,程序员必须在指针不再有用时显式地销毁该对象
int *ptr = new int;
...
delete ptr;
下面这种情况不需要
int A = 10;
int *ptr = &A; //因为这些是在栈上申请的,因为函数是在栈上进行展开,出了作用域还会自动释放
- 智能指针最重要的一个作用就是防止内存泄漏,(什么是内存泄漏?申请的空间没有释放。内存泄漏是指针丢了还是内存丢了?指针,通过指针可以找到这块内存,然后可以进行释放)
所以,普通指针丢了会导致内存泄露的问题。
另外一个作用就是解决异常安全问题,异常导致执行流乱跳,可能就会直接忽略释放动态内存的代码,这些可能就会造成内存泄漏。
智能指针说白了,就是用类封装出一个指针,这个指针出了作用域会自动调用析构释放资源,但平时开辟资源时,都是有一个指针指向这块空间的,有时对这块空间可能没有进行释放,或者释放时,空间还在使用,前者就有可能造成内存泄漏,但如果此时是智能指针的话,就不会有这种情况。
智能制指针的特性:
1.RAII(在初始化的时候把构造函数请求的资源保存起来,调用完毕之后,释放资源)```
2.本身它是对象,但是要能像指针一样--------所以要重载operator、->*
3.能赋值能拷贝
赋值(=),拷贝它既然是个类,能实例化出对象,那么对象1 = 对象2可否,此时这里是拷贝构造,因为没有=重载的成员函数
智能指针这个类和其他类最大的不同:
智能指针的发展--------解决它存在的潜拷贝问题
发展:c++98-----boost-----c++11(一代版本解决一代版本中的缺陷问题)
1 ..auto_ptr(c++98) --------------管理权转移,管理权转移的原因就是,赋值,拷贝的时候都是浅拷贝,浅拷贝析构两次出现问题
存在的问题:ap1的管理权 转移了,指向空,所以引用它的时候就问题了,禁止使用
template<class T>
class AutoPtr
{
public:
AutoPtr(T* ptr)
:_ptr(ptr)
{}
// ap2(ap1)
AutoPtr(AutoPtr<T>& ap)
:_ptr(ap._ptr)
{
ap._ptr = NULL;
}
// ap1 = ap3;
AutoPtr<T>& operator=(AutoPtr<T>& ap)
{
if (this != &ap)
{
delete _ptr; //释放原来的指向
_ptr = ap._ptr; //交换管理权,交付指向
ap._ptr = NULL; //指向置空
}
return *this;
}
~AutoPtr()
{
delete _ptr;
}
T& operator*()
{
return *_ptr;
}
T* operator->()
{
return _ptr;
}
protected:
T* _ptr;
};
- scoped_ptr(boost版本) --------------防拷贝,说白了,就是不让拷贝,既然智能指针存在这个拷贝的问题,那就不然拷贝了,只声明,不定义,就可以通过编译,防拷贝还是为了解决只智能指针拷贝,赋值时出现的潜拷贝的问题。对这两个成员函数只是声明,不定义,那么就不会出现拷贝的时候默认浅拷贝的问题了,
公司推荐使用 公司推荐使用
template<class T>
class ScopedPtr
{
public:
ScopedPtr(T* ptr) //构造的时候把资源保存下来
:_ptr(ptr)
{}
~ScopedPtr() //析构,清理资源,满足RAII
{
delete _ptr;
}
T& operator*() //像指针一样
{
return *_ptr;
}
T* operator->()
{
return _ptr;
}
// 类的防拷贝,不让一个类拷贝,只声明,不实现,如果不声明,就会使用默认的拷贝---浅拷贝
private: //给成私有,防止外部拿走后自行实现
ScopedPtr(const ScopedPtr<T>& ap); //拷贝构造函数
ScopedPtr<T>& operator=(const ScopedPtr<T>& ap); //赋值运算符重载
protected:
T* _ptr;
};
- shared_ptr(boost版本)
scoped_ptr,面试介意可以实现,但是在实际中scoped_ptr不让拷贝,但有时就是需要拷贝,赋值,所以就出现了shared_ptr,
它的核心就是引用计数,但是存在循环引用问题和线程安全问题,所以出现了weak_ptr----解决循环引用问题,辅助shared_ptr。
struct ListNode
{
Shared_ptr<ListNode> _next; //节点类型为Shared_ptr;
Shared_ptr<ListNode> _prev;
~ListNode()
{
cout << "~ListNode()" << endl;
}
};
template<class T>
class SharedPtr
{
public:
SharedPtr(T* ptr = NULL)
:_ptr(ptr)
, _pCount(new int(1)) //一创建就给加1
{}
~SharedPtr()
{
cout << "~SharedPtr()" << endl;
if (--(*_pCount) == 0) //只有一个还在管理的时候,才进行释放
{
delete _ptr;
delete _pCount;
}
}
T& operator*()
{
return *_ptr; //解引用返回的是对象
}
T* operator->()
{
return _ptr;
}
// sp2(sp1)
SharedPtr(SharedPtr<T>& sp) //拷贝构造函数
:_ptr(sp._ptr)
, _pCount(sp._pCount)
{
++(*_pCount);
}
// sp1 = sp2
SharedPtr<T>& operator=(const SharedPtr<T>& sp) //赋值 运算符重载,注意里面的坑
{
//if (this != &sp),这个不好,用下面的判断,
if (_ptr != sp._ptr) //指针不相等,说明不是同一个对象,不是自己被自己赋值
{
if (--(*_pCount) == 0) //有可能之前,s1已经有所指向了,即sp1,sp2之前分别指向两块空间
{ //所以先清空s1
delete _ptr;
delete _pCount;
}
_ptr = sp._ptr;
++(*sp._pCount);
_pCount = sp._pCount;
}
return *this;
}
protected:
T* _ptr;
int* _pCount; //引用计数
};
int main()
{
SharedPtr<ListNode> n1 = new ListNode; //new 一个ListNode 类型的对象交给智能指针对象管理
SharedPtr<ListNode> n2 = new ListNode;
n1->_next = n2; //调用重载的=,使得cout++ = 2
n2->_prev = n1;
}
shared_ptr的循环引用,什么是循环引用?
以双向列表为例,
SharedPtr<ListNode> n1 = new ListNode; //new 一个ListNode 类型的对象交给智能指针对象管理
SharedPtr<ListNode> n2 = new ListNode;
n1->_next = n2; //调用重载的
n2->_prev = n1;
n1,n2出了作用域后就释放,
**shared_ptr的线程安全问题 **
多线程问题就是对于多核来说,多个进程可以在同一时间同时访问某些资源,所以两个线程可能都取到2这个引用计数,然后就都减减到1,就都没有释放,造成资源泄漏。可以考虑给引用计数加锁
4. weak_ptr(boost版本)
解决循环引用的问题,如何解决的?
weak_ptr不增加shared_ptr的引用计数,保存的是shared_ptr的资源
struct ListNode //双向链表的类型是weak_ptr
{
WeakPtr<ListNode> _next;
WeakPtr<ListNode> _prev;
~ListNode()
{
cout << "~ListNode()" << endl;
}
};
template<class T>
class shared_Ptr
{
template<class>
friend class WeakPtr;
public:
//RAII
shared_Ptr(T *p)
:ptr(p)
{}
~shared_Ptr()
{
delete ptr;
}
//像指针一样
T &operator*(shared_Ptr<T> &s)
{
return *ptr;
}
shared_Ptr<T>&operator=(shared_Ptr<T> &s)
{
return ptr;
}
//拷贝str1(str2)
shared_Ptr(shared_Ptr<T> &s)
:ptr(s.ptr)
,count(s.count)
{
++(*count);
}
//str1=str2
shared_Ptr<T>& operator=(shared_Ptr<T> &s)
{
if (ptr != s.ptr)
{
if (--(*count) == 0) //如果之前有指向则结果减1
{
delete ptr; //清空之前所指的内容
delete count;
}
ptr = s.ptr;
++(*s.count); //为什么
count = s.count;
}
return *this;
}
private:
T* ptr;
T *count;
};
template<class T>
class weak_ptr
{
public:
WeakPtr()
:_ptr(NULL)
WeakPtr(SharedPtr<T>& sp) //拷贝构造跟其他不同,
:_ptr()
{}
WeakPtr<T>& operator=(SharedPtr<T>& sp) //关注重点
{
_ptr = sp._ptr;
return *this;
}
T& operator*()
{
return *_ptr;
}
T *operator->()
{
return _ptr;
}
private:
T* _ptr;
};
int main()
{
SharedPtr<ListNode> n1 = new ListNode;
SharedPtr<ListNode> n2 = new ListNode;
n1->_next = n2;
n2->_prev = n1;
ListNode* p = new ListNode;
delete p;
}
- c++11的智能指针
unique_ptr—相当于scoped_ptr
shared_ptr
weak_ptr - 查看一下库里的智能指针的使用方法,尤其是看重载[]
struct ListNode
{
weak_ptr<ListNode> _next;
weak_ptr<ListNode> _prev;
}
shared_ptr<ListNode> node1(new ListNode);
shared_ptr<ListNode> node2(new ListNode);
node1->_next = node2;
node2->_prev = node1;
cout<<node1.use_cout()<<endl;
仿函数
面试问的少
像函数,但它是一个对象,它调用的是operator();,这个重载的()就叫做防函数
struct Less
{
bool operator()(const T& x1,const T&x2)
{
return x1<x2;
}
}
Less<int> less;
cout<<less(1,2)<<endl; //仿函数
所以,当一个类中重载了(),这个类就是仿函数类