智能指针简单实现

173 阅读2分钟

在c++开发中,会存在一个问题,在申请内存之后可能会忘记释放,导致内存泄漏,下面文章会从实现简单智能指针到安卓智能指针进行分析

1.内存泄漏举例

实例化对象指针忘记使用delete从而导致内存泄漏

#include <iostream>
#include <string.h>

using namespace std;

class Student
{
public:
	Student()
	{
		cout << "Student()" << endl;
	}

	~Student()
	{
		cout << "~Student()" << endl;
	}

	void print_info(void)
	{
		cout << "just for test" << endl;
	}
};


void test_func(void)
{
	Student* stu = new Student();
	stu->print_info();
	//此处忘记使用delete stu来销毁对象,导致内存泄漏
}

int main(void)
{
	test_func();
}

结果:

Student()
just for test

2.改进方法1

在函数里面创建局部对象,在函数结束之后变量会自动销毁

#include <iostream>
#include <string.h>

using namespace std;

class Student
{
public:
	Student()
	{
		cout << "Student()" << endl;
	}

	~Student()
	{
		cout << "~Student()" << endl;
	}

	void print_info(void)
	{
		cout << "just for test" << endl;
	}
};


void test_func(void)
{
	Student stu;
	stu.print_info();
	
}

int main(void)
{
	test_func();
}

结果:

Student()
just for test
~Student()

对于局部变量来说,在函数结束之后,编译器会自动调用析构函数来销毁对象

3.改进方法2

局部变量和类指针结合解决未手动delete导致的内存泄漏

#include <iostream>
#include <string.h>

using namespace std;

class Student
{
public:
	Student()
	{
		cout << "Student()" << endl;
	}

	~Student()
	{
		cout << "~Student()" << endl;
	}

	void print_info(void)
	{
		cout << "just for test" << endl;
	}
};

class sp
{
private:
	Student* stu;
public:
	sp():stu(NULL)
	{
	
	}

	sp(Student* other)
	{
		cout << "sp()" << endl;
		stu = other;
	}

	~sp()
	{
		cout << "~sp()" << endl;
		if (stu)//如果stu存在,那么删除即可
		{
			delete stu;
		}
	}

	//重载指针运算符,从而可以访问到Student中的print_info对象
	Student* operator->()
	{
		return stu;
	}

};

void test_func(void)
{
	//以下会调用sp的构造函数sp(Student *other)
	//Student *tmp = new Student()
	//sp s(tmp)
	sp s = new Student();
	s->print_info();
	
}

int main(void)
{
	test_func();
}

结果:

Student()
sp()
just for test
~sp()
~Student()

第二种写法:

#include <iostream>
#include <string.h>

using namespace std;

class Student
{
public:
	Student()
	{
		cout << "Student()" << endl;
	}

	~Student()
	{
		cout << "~Student()" << endl;
	}

	void print_info(void)
	{
		cout << "just for test" << endl;
	}
};

class sp
{
private:
	Student* stu;
public:
	sp():stu(NULL)
	{
	
	}

	sp(Student* other)
	{
		cout << "sp()" << endl;
		stu = other;
	}

	sp(sp &other)
	{
		cout << "sp(sp &other)" << endl;
		stu = other.stu;
	}

	~sp()
	{
		cout << "~sp()" << endl;
		if (stu)//如果stu存在,那么删除即可
		{
			delete stu;
		}
	}

	//重载指针运算符,从而可以访问到Student中的print_info对象
	Student* operator->()
	{
		return stu;
	}

};

void test_func(sp &other)
{
	sp s = other;
	s->print_info();
	
}

int main(void)
{
	int i = 0;
	sp other = new Student();
	for (i = 0; i < 2; i++)
		test_func(other);
}

上面程序存在如下问题,当第一次调用test_func(other)时,other内部的stu指针和sp内部的指针指向同一个Student对象,当函数生命周期结束之后,会调用delete函数销毁Student对象,此时other对象已经不存在,那么下一次再调用test_func(other)函数之后会导致系统崩溃。所以需要考虑sp类内部的Student指针多次被调用的情况,解决方法是增加一个引用计数,当引用计数为0时才销毁sp类内部的stu指针

4.改进方法

使用引用计数保护对象不被过早的销毁

#include <iostream>
#include <string.h>

using namespace std;

class Student
{
private:
	unsigned int count;
public:
	Student():count(0)
	{
		cout << "Student()" << endl;
	}

	~Student()
	{
		cout << "~Student()" << endl;
	}

	void print_info(void)
	{
		cout << "just for test" << endl;
	}

	void inc_strong(void)
	{
		count++;
	}

	void dec_strong(void)
	{
		count--;
	}

	unsigned int get_strong_count(void)
	{
		return count;
	}
};

class sp
{
private:
	Student* stu;
public:
	sp():stu(NULL)
	{
	
	}

	sp(Student* other)
	{
		cout << "sp()" << endl;
		stu = other;
		stu->inc_strong();//引用次数加1
	}

	sp(sp &other)
	{
		cout << "sp(sp &other)" << endl;
		stu = other.stu;
		stu->inc_strong();
	}

	~sp()
	{
		cout << "~sp()" << endl;
		if (stu)
		{
			stu->dec_strong();
			if ((stu->get_strong_count() == 0))//如果stu存在,那么删除即可
			{
				delete stu;
				stu = NULL;
			}
		}
	}

	//重载指针运算符,从而可以访问到Student中的print_info对象
	Student* operator->()
	{
		return stu;
	}

};

void test_func(sp &other)
{
	sp s = other;
	s->print_info();
	
}

int main(void)
{
	int i = 0;
	sp other = new Student();
	for (i = 0; i < 2; i++)
		test_func(other);
}

优化:重载解引用操作符

	Student& operator*()
	{
		return *stu;
	}

改进五:将引用计数相关的操作抽离出来,并且将智能指针sp抽象为类模板

#include <iostream>
#include <string.h>

using namespace std;


class RefBase {
private:
	unsigned int count;
public:
	RefBase():count(0)
	{
	
	}

	void inc_strong(void)
	{
		count++;
	}

	void dec_strong(void)
	{
		count--;
	}

	unsigned int get_strong_count(void)
	{
		return count;
	}
};


class Student : public RefBase
{
public:
	Student()
	{
		cout << "Student()" << endl;
	}

	~Student()
	{
		cout << "~Student()" << endl;
	}

	void print_info(void)
	{
		cout << "just for test" << endl;
	}

};

template<typename T>
class sp
{
private:
	T* t;
public:
	sp():t(NULL)
	{
	
	}

	sp(T* other)
	{
		cout << "sp()" << endl;
		t = other;
		t->inc_strong();//引用次数加1
	}

	sp(sp &other)
	{
		cout << "sp(sp &other)" << endl;
		t = other.t;
		t->inc_strong();
	}

	~sp()
	{
		cout << "~sp()" << endl;
		if (t)
		{
			t->dec_strong();
			if ((t->get_strong_count() == 0))//如果stu存在,那么删除即可
			{
				delete t;
				t = NULL;
			}
		}
	}

	//重载指针运算符,从而可以访问到Student中的print_info对象
	T* operator->()
	{
		return t;
	}

	T& operator*()
	{
		return *t;
	}

};

template<typename T>
void test_func(sp<T> &other)
{
	sp s = other;
	s->print_info();
	
}

int main(void)
{
	int i = 0;
	sp<Student> other = new Student();
	for (i = 0; i < 2; i++)
		test_func(other);
}