优秀的编程知识分享平台

网站首页 > 技术文章 正文

理解并能自定义智能指针,C++算是入了个门

nanyue 2024-07-29 01:11:53 技术文章 8 ℃

智能指针涉及到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-

Tags:

最近发表
标签列表