C++智能指针

智能指针

在C++指针向来是最令人头疼的事情,我们来看一个栗子。

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

using namespace std;

class Person {
public:
	Person() {
		cout <<"Pserson()"<<endl;
	}
	~Person(){
		cout << "~Person()"<<endl;
	}
	void printInfo(void){
		cout<<"just a test function"<<endl;
	}
};

void test_func(void)
{
	Person *p = new Person();
	p->printInfo();
}

int main(int argc, char **argv)
{	
	int i;

	for (i = 0; i < 2; i++)
		test_func();
	return 0;
}

我们编译执行代码,输出如下结果:
在这里插入图片描述
可以看到代码执行了两次 Person() 构造函数,但是没有执行析构函数 ~Person() 函数对内存进行释放,这是非常危险的,代码会一直占用内存,这里才执行两次,如果执行成千上万次甚至可能导致代码崩溃,这时候需要用 delete 手动释放内存。我们对代码进行优化,修改 test_func(void) 函数:

void test_func(void)
{
	//Person *p = new Person();
	//p->printInfo();
	//delete p;
	Person per;
	per.printInfo();
}

我们将Person p替换成了局部变量Person per或者用delete p手动释放内存。编译执行,看一下效果:
在这里插入图片描述
可以看到test_func(void)函数执行结束之后会自动调用析构函数,解决了内存泄漏的问题,但如果团队合作是我们也不能阻止别人使用指针呀。

我们进一步对代码进行优,我们定义一个SP(SmartPointer) 类:

class sp {
private:
	Person *p;
public:
	sp() : p(0) {}
	
	sp(Person *other){
		cout<<"sp(const Person *other)"<<endl;
		p = other;
	}

	sp(sp &other){
		cout<<"sp(const Person *other)"<<endl;
		p = other.p;
	}

	~sp(){
		cout<<"~sp()"<<endl;
		if (p)
			delete p;
	}

	Person *operator->(){
		return p;
	}	
};

修改 test_func(void) 函数:

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

	//Person *p = new Person();
	//p->printInfo();
	//delete p;
}

可以看到我们用 sp s 代替了 Person p ,我们在 sp 类里面定义了Person p,但是s是一个局部变量在test_fun(void)结束后就会自动调用析构函数~sp(),在 ~sp() 中我们实现了对 Person p的释放。修改main函数:

int main(int argc, char **argv)
{	
	int i;

	/* sp other = new Person()==>相当于:
	 * 1. Person *p = new Person();
	 * 2. sp tmp(p);  ==> sp(Person *other)
	 * 3.
	 * 3.1 sp other(tmp);  ==> sp(sp &other2)
	       问题在于: sp &other2 = tmp; // 错误语法
	                 const sp& other2 = tmp; //ok
	 * 或
	 * 3.2 sp other(tmp ==> Person*);  ==>sp(Person *other)
	 */
	
	sp other = new Person();

	for (i = 0; i < 2; i++)
		test_func(other);
	return 0;
}

编译执行代码,啪,怎么出错了?具体原因已经在 main 函数里进行注释注释,简言之就是 test_func(other) 需要执行两次,函数出入参数为引用,第一次结束时 sp other 就已经被释放了,第二次执行的时候肯定会粗错了,我们只需要修改构造函数:

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

再次编译执行,这下成功了:
在这里插入图片描述
对于刚才编译出错的问题我们还可以运用其他方法优化解决,大致的思路是:在构造函数里对引用对象的计数值加一,析构函数里对构造函数的计数值减一,只有当引用对象的计数值为零时才把引用对象给销毁掉。此外我们应该少用 "Person " ; 用 “sp” 来代替 “Person *p” ,但如果我们要用*(sp).XXX* 这种格式,我们就必须对 “*“ 就行重载”。为了方便阅读,直接给出完整代码:

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

using namespace std;

class Person {
private:
	int count;

public:
	void incStrong(){ count++; }	
	void decStrong(){ count--; }	
	int getStrongCount(){ return count;}

	Person() : count(0){
		cout <<"Pserson()"<<endl;
	}

	~Person()
	{
		cout << "~Person()"<<endl;
	}
	void printInfo(void)
	{
		cout<<"just a test function"<<endl;
	}
};

class sp {
private:
	Person *p;

public:
	sp() : p(0) {}
	
	sp(Person *other)
	{
		cout<<"sp(Person *other)"<<endl;
		p = other;
		p->incStrong();
	}

	sp(const sp &other)
	{
		cout<<"sp(const sp &other)"<<endl;
		p = other.p;
		p->incStrong();
	}

	~sp()
	{
		cout<<"~sp()"<<endl;
		
		if (p)
		{
			p->decStrong();
			if (p->getStrongCount() == 0)
			{
				delete p;
				p = NULL;
			}
		}
	}

	Person *operator->()
	{
		return p;
	}

	Person& operator*()
	{
		return *p;
	}
	
};

void test_func(sp &other)
{
	sp s = other;

	cout<<"In test_func: "<<s->getStrongCount()<<endl;

	s->printInfo();

	//Person *p = new Person();
	//p->printInfo();
	//delete p;
}

int main(int argc, char **argv)
{	
	int i;

	/* 少用"Person *"; 用"sp"来代替"Person *"
	 * Person *per; 
	 * 有2种操作: per->XXXx, (*per).XXX
	 * sp也应该有这2中操作:
	 *            sp->XXX, (*sp).XXX
	 *
	 */
	sp other = new Person();

	(*other).printInfo();
	cout<<"Before call test_func: "<<other->getStrongCount()<<endl;

	for (i = 0; i < 2; i++)
	{
		test_func(other);
		cout<<"Af  ter call test_func: "<<other->getStrongCount()<<endl;
	}
	return 0;
}

编译执行代码,输出结果与推理一致:
在这里插入图片描述
我们的程序是否已经完美了,还有可以优化的地方吗?当然是有的,对于对引用对象计数进行加减部分我们可以将它定义成基类,直接继承;对于sp部分我们还可以将它修改成模板…

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

using namespace std;

class RefBase {
private:
	int count;

public:
	RefBase() : count(0) {}
	void incStrong(){ count++; }	
	void decStrong(){ count--; }	
	int getStrongCount(){ return count;}
};

class Person : public RefBase{

public:
	Person() {
		cout <<"Pserson()"<<endl;
	}


	~Person()
	{
		cout << "~Person()"<<endl;
	}
	void printInfo(void)
	{
		cout<<"just a test function"<<endl;
	}
};

template<typename T>
class sp {
private:
	T *p;

public:
	sp() : p(0) {}
	
	sp(T *other)
	{
		cout<<"sp(T *other)"<<endl;
		p = other;
		p->incStrong();
	}

	sp(const sp &other)
	{
		cout<<"sp(const sp &other)"<<endl;
		p = other.p;
		p->incStrong();
	}

	~sp()
	{
		cout<<"~sp()"<<endl;
		
		if (p)
		{
			p->decStrong();
			if (p->getStrongCount() == 0)
			{
				delete p;
				p = NULL;
			}
		}
	}

	T *operator->()
	{
		return p;
	}

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

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

	cout<<"In test_func: "<<s->getStrongCount()<<endl;

	s->printInfo();

	//Person *p = new Person();
	//p->printInfo();
	//delete p;
}

int main(int argc, char **argv)
{	
	int i;

	/* 少用"Person *"; 用"sp<Person>"来代替"Person *"
	 * Person *per; 
	 * 有2种操作: per->XXXx, (*per).XXX
	 * sp也应该有这2中操作:
	 *            sp->XXX, (*sp).XXX
	 *
	 */
	sp<Person> other = new Person();

	(*other).printInfo();
	cout<<"Before call test_func: "<<other->getStrongCount()<<endl;

	for (i = 0; i < 2; i++)
	{
		test_func(other);
		cout<<"After call test_func: "<<other->getStrongCount()<<endl;
	}
	return 0;
}

最后再试验一下:在这里插入图片描述

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值