所谓智能指针指的是一种用起来像指针但又无需关心内存管理的一种机制。
普通指针的问题
C/C++ 中指针极为常见,但普通指针最大的问题就在于需要程序员记住分配内存后要手动释放,否则就会有内存泄漏或者引用野指针的风险。
比如有这样一个简单的类:
class person {
public:
person() {}
~person(){ cout<<"delete person."<<endl;}
void display(){cout<<"This is person"<<endl;}
};
如果在堆上分配该对象的话,那么在使用完后必须调用 delete 释放内存:
person* p = new person();
...
delete p; // 使用完后手动释放内存
那么有没有一种更好的机制来 自动 管理在堆上分配的对象呢?像下面这样:
smart_pointer p(new person());
p->display();
// 无需手动调用 delete 释放内存
答案是肯定的。
让指针更智能
指针本质上就是一个内存地址,因此 普通指针根本就无法分辨什么时候该指针指向的内存使用完毕,
普通指针需要更聪明一点。
什么情况下一块内存就可以释放掉了?很简单,没人使用的时候就可以释放掉了。那么该怎么知道有没有人在使用一块内存?答案同样很简单,只需要记个数就可以了。
不知道大家去游乐园公园之类园区时有没有注意过,这些地方通常会显示园区里现在有多少人,有人进去这个数字就加 1,有人出来这个数字就减一,什么时候管理员就可以关门了?当然是园区里没人的时候。
智能指针本质上就是园区里显示人数的指示牌,只不过在这里不叫人数,而是叫引用计数,Reference counting。
带有引用计数的指针:智能指针
普通指针本身并不能告诉我们是否指向的内存还有没有用,因此需要额外信息,这里的额外信息就是引用计数,将引用计数和智能指针组合在一起就是智能指针:
template<typename T>
class smart_pointer
{
T* data; // 智能指针指向的内存
int count = 0; // 引用计数
};
创建智能指针时如果传入的是内存地址,那么我们需要将引用计数设置为 1,因为这是智能指针首次指向某个内存地址,智能指针构造函数如下:
smart_pointer(T* per){
data = per;
count = 1;
}
当然智能指针的构造函数也可以传入另一个智能指针,这时我们必须在原有引用计数的基础上加一,因为此时多了一个对该内存的引用,智能指针拷贝构造如下:
smart_pointer(const smart_pointer<T>& p){
data = p.data;
count=p.count;
++count;
}
当智能指针使用完毕被析构时就好比有人离开园区,这时必须判断引用计数是否为 0,如果为 0 那么我们应该关闭园区:
~smart_pointer(){if ((--count) == 0) // 没有人在园区了
delete data;
}
当然,智能指针首先用起来必须和普通指针一样,因此我们需要重载指针的引用与解引用:
T& operator*(){return *data;}
T* operator->(){return data;}
最后,不要忘了指针可以被赋值,因此智能指针也应该支持赋值,这样我们需要重载赋值运算符。由于指针被赋值后会指向新的内存,因此在智能指针指向新的内存前必须离开当前所在的园区:
smart_pointer<T> operator=(const smart_pointer<T>& p){if(this == &p)
return *this; // 是自己人
if ((--count) == 0)
delete data; // 没有人在这个园区了
data = p.data; //
count = p.count; // 进到新园区
++count; //
return *this;
}
应用
简单的使用一下刚刚发明的新指针 :)
void test_smartpointer(){smart_pointer<person> p(new person("tom",30));
p->display();
smart_pointer<person> q = p;
q->display();
smart_pointer<person> m(q);
m->display();}
一切工作正常。
总结
希望这篇简短的介绍能对大家理解智能指针有所帮助。当然,在真实项目中大家要使用 C ++ 标准定义的智能指针。
如果你喜欢这篇文章,欢迎关注微信公共账号 码农的荒岛求生 获取更多相关内容。
计算机内功决定程序员职业生涯高度