智能指针
输入问题: 给定一个字符串,不知道有几个字符,怎样输入? 使用cin.getline(); 输入一行。
智能指针概念
智能指针主要是针对裸指针进行了一次面向对象的封装,在构造函数中初始化资源地址,在析构函数中释放资源。当资源应该被释放时,指向它的智能指针可以确保自动释放它。
RAII
RAII 对堆上空间进行自动化管理——利用对象自动析构的机制。
malloc, new 等动态分配的对象,很有可能忘记了去释放资源而导致泄露。 对于一个对象而言,在构造函数的时候申请空间,而在析构函数(在离开作用域时调用)的时候释放空间,也就是RAII资源获取即初始化技术。
C++11引入智能指针,使用引用计数,让程序员不再需要关心手动释放内存。
引用计数
这种计数是为了防止内存泄露而产生的。
对于动态分配的对象,进行引用计数,每当增加一次对同一个对象的引用,那么引用计数就会增加一次,每删除一次引用,引用计数就会减一,当一个对象的引用计数为0时,就会自动删除指向的堆内存。
注意:引用计数不是垃圾回收,引用计数能够尽快收回不再使用的对象,同时在回收过程中也不会造成长时间的等待,更能够清晰的表明资源的生命周期。
裸指针
int *ptr;
这种指针被称为裸指针。 使用裸指针会存在一些不足:
-
如果使用裸指针分配内存后,忘记手动释放资源,会出现内存泄漏。 -
如果使用多个裸指针指向同一资源,其中一个指针对资源进行释放,其他指针成为空悬指针,如果再次释放会存在错误。 -
如果程序异常退出时,裸指针的释放资源的代码未能执行,也会造成内存泄漏。
智能指针的分类
需要引入头文件#include< memory >智能指针
不带引用计数的智能指针auto_ptr
已经废弃
- 不能使用同一个裸指针赋值/初始化多个auto_ptr;
- 拷贝构造和等号运算符—— 将原智能指针置空;
- 不允许隐式构造;
auto_ptr是通过将除最后一个以外的其它auto_ptr 置 nullptr 来避免浅拷贝的发生,它的资源所有权是可以转移的。
void fun()
{
int* p = new int(10);
auto_ptr<int> a_p(p);
auto_ptr<int> a_p = auto_ptr<int>(p);
cout << *p << endl;
cout << *a_p << endl;
a_p.release();
a_p.reset();
a_p.get();
}
//auto_ptr a_p = p; 拷贝构造 不允许隐式构造,根据类型自推
*p生成临时auto_ptr对象——隐式构造
* 使用临时对象拷贝构造a_p
* 析构临时对象
* ——优化-- 直接构造a_p
auto_ptr a_p = auto_ptr §; 显式构造
模拟实现mauto_ptr
#ifndef MAUTO_PTR_H
#define MAUTO_PTR_H
template<typename T>
class Mauto_ptr
{
public:
explicit Mauto_ptr(T*ptr=nullptr)
:_ptr(ptr)
{}
Mauto_ptr(Mauto_ptr& src)
:_ptr(src.release())
{}
Mauto_ptr& operator=(Mauto_ptr& src)
{
_ptr = src.release();
}
~Mauto_ptr()
{
delete_ptr;
}
T* release()
{
T* tmp = _ptr;
_ptr = NULL;
return tmp;
}
void reset()
{
delete_ptr;
_ptr = NULL;
}
T& operator*()
{
return *_ptr;
}
T* operator->()
{
return _ptr;
}
private:
T* _ptr;
};
#endif
不带引用计数的智能指针unique_ptr
C++ 11
- 不能使用同一个裸指针赋值/初始化多个unique_ptr;
- 不允许隐式构造;
- 不允许拷贝构造,不允许等号运算符重载;
unique_ptr 删除了拷贝构造和赋值函数,因此不支持普通的拷贝或赋值操作。 引入了带有右值引用的拷贝构造和等号运算符重载,可以把 unique_ptr 作为函数的返回值。
unique_ptr<int>fun(unique_ptr<int>& ptr)
{
cout << *ptr << endl;
int* p = new int(9);
return unique_ptr<int>(p);
}
void fun()
{
int* p = new int(10);
unique_ptr<int> u_p;
unique_ptr<int> u_p(p);
cout << *p << endl;
cout << *u_p << endl;
u_p.get();
u_p.release();
u_p.reset();
unique_ptr<int> u_p2(fun(u_p));
u_p2 = fun(u_p);
cout << *u_p2 << endl;
}
右值引用 用来引用即将死亡的对象——临时对象
const int& a = 10;
int&& b = 10;
模拟实现unique_ptr
#ifndef MUNIQUE_PTR_H
#define MUNIQUE_PTR_H
template<typename T>
class Munique_ptr
{
public:
explicit Munique_ptr(T* ptr = nullptr)
:_ptr(ptr)
{}
Munique_ptr(Munique_ptr&& src)
:_ptr(src.release())
{
}
Munique_ptr& operator=(Mauto_ptr&& src)
{
_ptr = src.release();
}
~Munique_ptr()
{
delete_ptr;
}
T* release()
{
T* tmp = _ptr;
_ptr = NULL;
return tmp;
}
void reset()
{
delete_ptr;
_ptr = NULL;
}
T& operator*()
{
return *_ptr;
}
T* operator->()
{
return _ptr;
}
operator bool()
{
return _ptr != NULL;
}
private:
T* _ptr;
};
#endif
带引用计数的智能指针shared_ptr——强智能指针
- 不允许隐式构造;
- 不能使用同一个裸指针赋值/初始化多个shared_ptr;
- 允许拷贝构造,允许等号运算符重载;
一个shared_ptr 对资源进行引用时,资源的引用计数会增加一,通常用于管理对象的生命周期。只要有一个指向对象的shared_ptr 存在,该对象就不会析构。
#include<iostream>
#include<map>
#include<memory>
#include"mshared_ptr.h"
using namespace std;
template<typename T>
map<T*, int> Mshared_ptr<T>::_count = new map<T*,int>();
void fun()
{
int* p = new int(11);
shared_ptr<int> s_p(p);
shared_ptr<int> s_p1(s_p);
s_p = s_p1;
cout << s_p.use_count() << endl;
cout << *p << endl;
cout << *s_p << endl;
cout << *s_p1 << endl;
s_p.unique();
}
模拟实现shared_ptr
#ifndef MSHARED_PTR_H
#define MSHARED_PTR_H
template<typename T>
class Mshared_ptr
{
public:
explicit Mshared_ptr(T* ptr = nullptr)
:_ptr(ptr)
{}
Mshared_ptr(Mshared_ptr& src)
{
_count.insert(make_pair(_ptr, 1));
_count[_ptr]++;
_ptr = src._ptr;
}
Mshared_ptr& operator=(Mshared_ptr&& src)
{
if (_ptr == src._ptr)
{
return *this;
}
if (unique())
{
_count.erase(_ptr);
delete _ptr;
}
else
{
_count[_ptr]--;
}
_ptr = src._ptr;
}
~Mshared_ptr()
{
if (unique())
{
_count.erase(_ptr);
delete _ptr;
}
else
{
_count[_ptr]--;
}
_ptr = src._ptr;
}
bool unique()
{
if (count.find(_ptr) == count.end()||count[_ptr]==1)
{
return true;
}
return false;
}
T* release()
{
T* tmp = _ptr;
if (unique())
{
_count.erase(_ptr);
}
else
{
_count[_ptr]--;
}
_ptr = NULL;
return tmp;
}
void reset()
{
if (unique())
{
_count.erase(_ptr);
delete _ptr;
}
else
{
_count[_ptr]--;
}
_ptr = NULL;
}
T& operator*()
{
return *_ptr;
}
T* operator->()
{
return _ptr;
}
operator bool()
{
return _ptr!=NULL;
}
private:
static map<T*, int>* _count;
T* _ptr;
};
带引用计数的智能指针weak_ptr——弱智能指针
- 不能直接使用,不能解引用;
- 如果要使用必须先转化为强智能指针,用lock()返回强智能指针;
- 弱智能指针不占用引用计数;
- 只能由强智能指针构造弱智能指针;
weak_ptr 对资源的引用不会引起资源的引用计数的变化,通常作为观察者,用于判断资源是否存在,并根据不同情况做出相应的操作。
比如使用 weak_ptr 对资源进行弱引用,当调用 weak_ptr的 lock() 方法时,若返回 nullptr ,则说明资源已经不存在,放弃对资源的继续操作。否则,将返回一个 shared_ptr 对象,可以继续操作资源。
一旦最后一个指向对象的 shared_ptr 被销毁,对象就会被释放。即使有 weak_ptr 指向对象,对象也还是会被释放。
class A
{
public:
A()
{
cout << "A()" << endl;
}
~A()
{
cout << "~A()" << endl;
}
weak_ptr<B> _ptr_b;
};
class B
{
public:
B()
{
cout << "B()" << endl;
}
~B()
{
cout << "~B()" << endl;
}
weak_ptr<A> _ptr_a;
};
void fun()
{
shared_ptr<A> a_p(new A());
shared_ptr<B> b_p(new B());
shared_ptr<int> p(new int(10));
weak_ptr<int> w_p(p);
cout << *p << endl;
cout << *(w_p.lock()) << endl;
shared_ptr<int> tmp = w_p.lock();
a_p->_ptr_b = b_p;
b_p->_ptr_a = a_p;
}
模拟实现mweak_ptr
#ifndef MWEAK_PTR_H
#define MWEAK_PTR_H
#include"mshared_ptr.h"
template<typename T>
class Mweak_ptr
{
public:
Mweak_ptr(Mshared_ptr& s_ptr)
{
_s_ptr = s_ptr.get();
Mshared_ptr<T>::_count.insert(make_pair(_ptr, 1));
}
Mshared_ptr<T> lock()
{
if (Mshared_ptr<T>::_count.find(_s_ptr) != Mshared_ptr<T>::_count.end())
{
return Mshared_ptr<T>(_s_ptr);
}
return Mshared_ptr<T>();
}
private:
T* _s_ptr;
};
#endif
|