什么是智能指针
管理内存主要两种方式
- 智能指针
- 垃圾回收
各有优缺点,这里主要谈谈智能指针
我们为什么要使用智能指针
在多个指针指向同一块内存时候,如果A指针释放了内存,导致B指针指向的内存无效,那么再使用B指针时候就会出现未知的错误
C++提供的智能指针
C++98的方案中的 auto_ptr (已经被弃用)
auto_ptr<string> p1 (new string ("p1"));
auto_ptr<string> p2;
p2 = p1;
auto_ptr 是独占式的, 拥有权已经移交给了p2,所以在使用p1的时候回报错, 这种设计是不合理的。
C++11里的智能指针
- std::unique_ptr (C++11 独占式和auto_ptr作用相同, 比auto_ptr更安全)
- std::shared_ptr (C++11)
- std::weak_ptr (C++11)
- std::make_shared (C++11)
- std::make_unique (C++14)
这些都是线程安全的
std::shared_ptr
P1 P2 P3 引用对象的时候,会增加引用计数, P1释放的时候会减少引用计数。如果引用计数等于0,则删除实际的内存对象。
class D; //声明
class C
{
public:
shared_ptr<D> pd_;
~C()
{
std::cout << "C deleten";
}
};
class D
{
public:
shared_ptr<C> pc_;
D()
{
std::cout << "D::D()n";
}
~D()
{
std::cout << "D deleten";
}
};
void Test()
{
std::shared_ptr<int> sp0(new int(1));
std::shared_ptr<int> sp1(new int(2));
cout << " sp0.use_count " << sp0.use_count() <<endl;
std::shared_ptr<int> sp2 = sp1;
sp2 = sp0;
cout << " sp1.use_count " << sp1.use_count() << endl;
cout << " sp2.use_count " << sp2.use_count() << endl;
{
std::shared_ptr<C> pc(new C());
std::shared_ptr<D> pd(new D());
cout << " pc.use_count " << pc.use_count() << endl;
cout << " pd.use_count " << pd.use_count() << endl;
pc->pd_ = pd;
pd->pc_ = pc;
cout << " pc.use_count " << pc.use_count() << endl;
cout << " pd.use_count " << pd.use_count() << endl;
}
}
此代码pc pd在离开作用域不会删除,因为shared_ptr 有循环引用的缺陷。 std::weak_ptr 这里就会派上用途。 改写为:
class D
{
public:
weak_ptr<C> pc_;
D()
{
std::cout << "D::D()n";
}
~D()
{
std::cout << "D deleten";
}
};
weak_ptr是弱智能指针对象,它不控制所指向对象生存期的智能指针,它指向由一个shared_ptr管理的智能指针。
- weak_ptr 使用 lock 获得一个可用的 shared_ptr 对象, weak_ptr 在使用前需要检查合法性.
- expired 用于检查对象是否已经释放, 如果已经释放, 返回 true; 否则返回 false.
- lock 用于获取对象的强引用(shared_ptr). 如果 expired 为 true, 返回一个空的 shared_ptr; 否则返回一个 shared_ptr。
- use_count 返回与 shared_ptr 共享引用计数.
- weak_ptr 支持拷贝或赋值, 但不会影响对应的 shared_ptr 内部对象的计数.
shared_ptr<string> p = make_shared<string>("c++");
weak_ptr<string> wp1(p);
if (shared_ptr<string> smptr = wp1.lock())
{
cout << "the shared_ptr is validn";
}
wp1.reset();
if (!shared_ptr<string> smptr = wp1.lock())
{
cout << "the shared_ptr is Invalidn";
}
std::unique_ptr 对象始终是关联的原始指针的唯一所有者。我们无法复制unique_ptr对象,它只能移动。
std::unique_ptr<string> uniquePtr(new string("c++"));
std::unique_ptr<string> uniquePtr1 = uniquePtr; //error
std::unique_ptr<string> uniquePtr1 = std::move(uniquePtr);//所有权只能转移
uniquePtr = std::make_unique<string>("c");
std::make_shared是一个模板函数,用于创建智能指针对象。
我们在写一代码时候
void TestMakeShare(const std::shared_ptr<D>& c, const std::shared_ptr<E> & e){}
C++ 不能保值参数的求值顺序。如果循序为
- D()
- E() //异常跳出
- shared_ptr<D>
- shared_ptr<E>
在2的位置异常了, 那么D()内存就泄漏了。
std::make_shared 可以解决内存泄漏问题。
TestMakeShare(std::make_shared<D>(new D()), std::make_shared<E>(new E()));
优点
- 提高性能 (一次内存申请)
- 异常安全 (防止内存泄漏)
缺点
- 构造函数必须是public
- 对象的内存可能无法及时回收(控制块和对象分配在一起)
实现shared_ptr weak_ptr unique_ptr仅供学习
/*********************************************************************************
*Copyright(C),Your Company
*FileName: SharePtr.h
*Author: BillMarshall
**********************************************************************************/
#pragma once
#include <iostream>
using namespace std;
namespace XCommon
{
//智能指针配置块
class ControlBlock
{
public:
ControlBlock() : _uses(0), _weaks(0) {};
int _uses; //强引用计数
int _weaks; //弱引用计数
};
template <class T>
class WeakPtr;
template <class T>
class SharePtr
{
public:
SharePtr(T* p = 0) : _ptr(p)
{
//这里最好使用小对象分配器
cnt = new ControlBlock();
if (p)
cnt->_uses = 1;
}
~SharePtr()
{
release();
}
int use_count()
{
return cnt->_uses;
}
SharePtr(SharePtr<T> const& s)
{
_ptr = s._ptr;
(s.cnt)->_uses++;
cnt = s.cnt;
}
//用过WeakPtr构造SharePtr
friend class WeakPtr<T>;
SharePtr(WeakPtr<T> const& w)
{
_ptr = w._ptr;
(w.cnt)->_uses++;
cnt = w.cnt;
}
//赋值构造函数
SharePtr<T>& operator=(SharePtr<T>& s)
{
if (this != &s)
{
//先释放自己
release();
(s.cnt)->_uses++;
cnt = s.cnt;
_ptr = s._ptr;
}
return *this;
}
T& operator*()
{
return *_ptr;
}
T* operator->()
{
return _ptr;
}
void Debug()
{
cout << "SP:" << cnt->_uses << " WP:" << cnt->_weaks << endl;
}
protected:
void release()
{
cnt->_uses--;
if (cnt->_uses < 1)
{
//强引用为0则删除
delete _ptr;
if (cnt->_weaks < 1)
{
//弱引用为0则删除配置块
delete cnt;
cnt = NULL;
}
}
}
private:
T* _ptr; //对象指针
ControlBlock* cnt; //配置块
};
//用来解决ShaderPtr引用循环问题
template <class T>
class WeakPtr
{
public:
WeakPtr()
{
_ptr = 0;
cnt = 0;
}
//WeakPtr只能通过SharePtr构造
WeakPtr(SharePtr<T>& s) : _ptr(s._ptr), cnt(s.cnt)
{
cnt->_weaks++;
}
WeakPtr(WeakPtr<T>& w) : _ptr(w._ptr), cnt(w.cnt)
{
cnt->_weaks++;
}
~WeakPtr()
{
release();
}
WeakPtr<T>& operator=(WeakPtr<T>& w)
{
if (this != &w)
{
release();
cnt = w.cnt;
cnt->_weaks++;
_ptr = w._ptr;
}
return *this;
}
WeakPtr<T>& operator=(SharePtr<T>& s)
{
release();
cnt = s.cnt;
cnt->_weaks++;
_ptr = s._ptr;
return *this;
}
//在使用WeakPtr时候转化为SharePtr对象
SharePtr<T> lock()
{
if (!expired())
{
return SharePtr<T>(*this);
}
return nullptr;
}
//判断SharePtr对象是否已经别释放掉
bool expired()
{
if (cnt)
{
if (cnt->_uses > 0)
{
return false;
}
}
return true;
}
friend class SharePtr<T>;
void Debug()
{
cout << "SP:" << cnt->_uses << " WP:" << cnt->_weaks << endl;
}
protected:
void release()
{
if (cnt)
{
//只删除弱引用控制块
cnt->_weaks--;
if (cnt->_weaks < 1 && cnt->_uses < 1)
{
delete _ptr;
cnt = NULL;
}
}
}
private:
T* _ptr;
ControlBlock* cnt;
};
template<typename T>
class UniquePtr
{
public:
UniquePtr(T* pResource = NULL)
: _ptr(pResource)
{
}
~UniquePtr()
{
del();
}
void reset(T* newPtr)
{
del();
_ptr = newPtr;
}
T* release()
{
T* pTemp = _ptr;
_ptr = nullptr;
return pTemp;
}
T* get()
{
return _ptr;
}
void Own(UniquePtr<T>& uPtr)
{
_ptr = uPtr._ptr;
uPtr._ptr = nullptr;
}
operator bool() const
{
return _ptr != nullptr;
}
T& operator * ()
{
return *_ptr;
}
T* operator -> ()
{
return _ptr;
}
private:
void del()
{
if (nullptr == _ptr) return;
delete _ptr;
_ptr = nullptr;
}
UniquePtr(const UniquePtr&) = delete; // 禁用拷贝构造
UniquePtr& operator = (const UniquePtr&) = delete; // 禁用拷贝赋值
private:
//对象是唯一的不需要引用配置块
T* _ptr;
};
}