智能指针涉及到C++的诸多核心语法现象:如指针、类对象、模板、重载、设计模式(代理模式)等,如果你能理解并能实现C++的智能指针,C++也就算入了个门。
1 为什么需要智能指针
1.1 有些数据要放到堆内存,堆内存数据没有scope,堆内存需要程序员自己管理;
智能指针管理堆空间对象(而不是栈空间对象,如果是栈空间对象,会有二次析构错误)。
1.2 避免写太多堆内存管理代码(何时何地delete,以及可能被跳过而不被执行,如异常处理或函数返回);
1.3 避免new与delete不匹配;
1.4 避免异常处理而不会执行delete语句。
#include <iostream>
using namespace std;
void test()
{
throw 666;
}
int main()
{
try{
int* p = new int();
test();
delete p; //得不到执行
} catch(...){
}
return 0;
}
2 智能指针的实现
2.1 封装裸指针
栈内存可以自动释放,类对象能够自动执行析构函数,两者一结合,便可以通过封装裸指针来实现裸指针指向堆内存的自动释放。
#include <iostream>
#include <memory>
using namespace std;
class Person{
public:
Person(int i):m_age(i){
cout<<"Person()"<<endl;
}
~Person(){
cout<<"~Person()"<<endl;
}
void run(){
cout<<"run!"<<endl;
}
private:
int m_age;
};
template <typename T>
class SmartPointer{
private:
T* m_ptr;
public:
SmartPointer(T* obj):m_ptr(obj){}
~SmartPointer(){
if(m_ptr==NULL) return;
delete m_ptr;
}
};
int main()
{
{
SmartPointer<Person> p(new Person(20));
}
getchar();
return 0;
}
/*Output:
Person()
~Person()
*/
2.2 通过运算符重载来让对象模拟指针行为
#include <iostream>
#include <memory>
using namespace std;
class Person{
public:
Person(int i):m_age(i){
cout<<"Person()"<<endl;
}
~Person(){
cout<<"~Person()"<<endl;
}
void run(){
cout<<"run!"<<endl;
}
private:
int m_age;
};
template <typename T>
class SmartPointer{
private:
T* m_ptr;
public:
SmartPointer(T* obj):m_ptr(obj){}
~SmartPointer(){
if(m_ptr==NULL) return;
delete m_ptr;
}
T* operator->(){ // 运算符重载
return m_ptr;
}
};
int main()
{
{
SmartPointer<Person> p(new Person(20));
p->run();
}
getchar();
return 0;
}
/*Output:
Person()
run!
~Person()
*/
3 C++标准库实现的各类智能指针
auto_ptr不能支持数组,因为其析构函数写死了delete语句,而不是delete[]语句,而shared_ptr可以:
shared_ptr<CObj[]> p(new CObj[11]);
另外,auto_ptr不能解决堆内存所有权的问题,也就是当有多个指针指向同一块堆内存时,由哪一个指针delete?哪一个指针拥有该块内存的所有权而能执行delete?解决的办法有:
3.1 深拷贝,不同的指针指向不同的堆内存空间;
3.2 所有权转换,保持所有权唯一,如unique_ptr,当堆内存指针存在复制时,原先指向该堆内存的指针置空,也就是发生所有权转换,最终确保只有一个指针指向一块堆内存。
3.3 引用计数,如shared_ptr,当只有一个指针指向某块堆内存时,最后拥有该块堆内存的指针拥有该块堆内存的所有权,执行delete。
3.4 shared_ptr需要解决循环引用的问题,weak_ptr让一个强引用变成一个弱引用,从而不会影响引用计数。
#include <iostream>
#include <memory>
using namespace std;
class Person;
class Car{
public:
//shared_ptr<Car> m_car;
weak_ptr<Car> m_car;
Car()
{
cout<<"Car()"<<endl;
}
~Car()
{
cout<<"Car()"<<endl;
}
};
class Person{
public:
//shared_ptr<> m_person;
shared_ptr<> m_person;
Person()car
{
cout<<"Person()"<<endl;
}
~Person()
{
cout<<"Person()"<<endl;
}
};
int main()
{
shared_ptr<Person> car(new Person());
shared_ptr<Car> person(new Car());
person->m_car = car;
car->m_person = person;
cout<<""<<endl;
getchar();
return 0;
}
循环引用的强引用与弱引用:
4 shared_ptr的不完整实现
#include <iostream>
#include <windows.h> // InterlockedIncrement
using namespace std;
#define SAFE_DELETE(p) if (p) { delete p; p = NULL; }
class KRefCount // 封装了一个整数及其增1和减1操作的计数器对象
{
public:
KRefCount():m_nCount(0){}
public:
unsigned AddRef() // 保证在一个线程访问变量时其它线程不能访问
{
return InterlockedIncrement(&m_nCount);
}
unsigned Release()
{
return InterlockedDecrement(&m_nCount);
}
void Reset()
{
m_nCount = 0;
}
private:
long m_nCount;
};
template <typename T> // 模板
class SmartPtr // 封装了一个指针及计数器对象
{
public:
SmartPtr(void)
: m_pData(NULL)
{
m_pReference = new KRefCount();
m_pReference->AddRef();
cout<<"智能指针空构造!"<<endl; // for test
}
SmartPtr(T* pValue)
: m_pData(pValue)
{
m_pReference = new KRefCount();
m_pReference->AddRef();
cout<<"智能指针参数构造!"<<endl; // for test
}
SmartPtr(const SmartPtr<T>& sp)
: m_pData(sp.m_pData)
, m_pReference(sp.m_pReference)
{
m_pReference->AddRef();
cout<<"智能指针复制构造!"<<endl; // for test
}
~SmartPtr(void)
{
if (m_pReference && m_pReference->Release() == 0)
{
SAFE_DELETE(m_pData);
SAFE_DELETE(m_pReference);
cout<<"智能指针析造!"<<endl; // for test
}
}
// 操作符重载
inline T& operator*()
{
return *m_pData;
}
inline T* operator->()
{
return m_pData;
}
SmartPtr<T>& operator=(const SmartPtr<T>& sp)
{
if (this != &sp)
{
if (m_pReference && m_pReference->Release() == 0)
{
SAFE_DELETE(m_pData);
SAFE_DELETE(m_pReference);
}
m_pData = sp.m_pData;
m_pReference = sp.m_pReference;
m_pReference->AddRef();
}
return *this;
}
SmartPtr<T>& operator=(T* pValue)
{
if (m_pReference && m_pReference->Release() == 0)
{
SAFE_DELETE(m_pData);
SAFE_DELETE(m_pReference);
}
m_pData = pValue;
m_pReference = new KRefCount;
m_pReference->AddRef();
return *this;
}
T* Get() // 返回智能指针对象的裸指针
{
T* ptr = NULL;
ptr = m_pData;
return ptr;
}
void Attach(T* pObject)// 将裸指针绑定到智能指针对象
{
if (m_pReference->Release() == 0)
{
SAFE_DELETE(m_pData);
SAFE_DELETE(m_pReference);
}
m_pData = pObject;
m_pReference = new KRefCount;
m_pReference->AddRef();
}
T* Detach()// 返回智能指针对象的裸指针交将计数器置0
{
T* ptr = NULL;
if (m_pData)
{
ptr = m_pData;
m_pData = NULL;
m_pReference->Reset();
}
return ptr;
}
private:
KRefCount* m_pReference; // 计数器对象
T* m_pData; // 裸指针
};
class CTest // for test
{
public:
CTest(int b) : a(b) {}
int a;
};
int main()
{
{ // local scope for test
SmartPtr<CTest> pSmartPtr1(new CTest(10));
SmartPtr<CTest> pSmartPtr2(new CTest(20));
SmartPtr<CTest> pSmartPtr3(pSmartPtr2);
pSmartPtr1 = pSmartPtr2;
cout<<(*pSmartPtr1).a+(*pSmartPtr2).a<<endl; // 40
}
system("pause");
}
/*
Output:
智能指针参数构造!
智能指针参数构造!
40
智能指针析造!
*/
-End-