转自 http://blog.csdn.net/ruizeng88/article/details/6691191
Why Smart Pointer?
为什么需要智能指针?因为c++的内存管理一直是个令人头疼的问题。
假如我们有如下person对象:每个person有自己的名字,并且可以告诉大家他叫什么名字:
- //
- //a person who can tell us his/her name.
- //
- #include<iostream>
- #include<string>
- using namespace std;
- class person{
- public:
- person(string);
- void tell();
- ~person();
- private:
- string name;
- };
- person::person(string name):name(name){
- }
- void person::tell(){
- cout << "Hi! I am " << name << endl;
- }
- person::~person(){
- cout << "Bye!" << endl;
- }
- #include "person.h"
- int main(){
- person *p = new person("Cici");
- p -> tell();
- delete p;
- }
简单的程序我们当然不会忘记释放堆中分配的对象。但是当程序复杂时,成千上万对象被动态的创建,而在不使用这些对象时,都需要及时的释放,否则就会造成内存泄露(memory leak)。内存泄露是c++程序中最常见的问题之一,因为c++本身并没有提供自动堆内存管理的功能,所以,所有这些任务都交给了程序员自己--程序员必须对自己动态创建的堆对象负责:在对象不需要被使用时及时地销毁对象。然而,程序员不是万能的,所以内存泄露,总是成为c++程序的常见bug。
这里不得不提到的是java,java增加了垃圾回收机制(garbage collection),jvm会自己管理那些不被使用到的对象并及时销毁他们,这大大减轻了程序员的负担,程序员只需要new自己需要的对象而不必去释放他们,因为,在对象不被使用(被引用)时,垃圾回收器会替我们清理残骸。java也因为这一特性而广受关注。
STL auto_ptr
c++程序员们当然也不甘寂寞,stl中有了“智能指针”:stl::auto_ptr。使用智能指针,我们就可以不必关心对象的释放,因为智能指针会帮助我们完成对象内存空间的释放。有了auto_ptr,我们的程序可以这样写了:
- #include "person.h"
- #include<memory>
- using namespace std;
- int main(){
- auto_ptr<person> p(new person("Cici"));
- p -> tell();
- //we don't have to delete p because smart pointer will handle this
- //delete p;
- }
可以看到输出和我们的第一个版本一模一样。虽然我们并没有delete我们创建的对象,但是可以看到,它已经在程序退出之前被正确的析构了。
- //
- //our simple smart pointer
- //
- #include "person.h"
- class smart_ptr{
- public:
- smart_ptr(person* p);
- ~smart_ptr();
- person& operator*();
- person* operator->();
- private:
- person *ptr;
- };
- smart_ptr::smart_ptr(person* p):ptr(p){
- }
- smart_ptr::~smart_ptr(){
- delete ptr;
- }
- person& smart_ptr::operator*(){
- return *ptr;
- }
- person* smart_ptr::operator->(){
- return ptr;
- }
- #include "smart_ptr.h"
- using namespace std;
- int main(){
- smart_ptr p(new person("Cici"));
- p -> tell();
- //we don't have to delete p because smart pointer will handle this
- //delete p;
- }
- //
- //our simple smart pointer
- //
- template <typename T>
- class smart_ptr{
- public:
- smart_ptr(T* p);
- ~smart_ptr();
- T& operator*();
- T* operator->();
- private:
- T* ptr;
- };
- template <typename T>
- smart_ptr<T>::smart_ptr(T* p):ptr(p){
- }
- template <typename T>
- smart_ptr<T>::~smart_ptr(){
- delete ptr;
- }
- template <typename T>
- T& smart_ptr<T>::operator*(){
- return *ptr;
- }
- template <typename T>
- T* smart_ptr<T>::operator->(){
- return ptr;
- }
引用计数
我们的smart_ptr是不是完美了呢?看看下面这种情况:
- #include "person.h"
- #include "smart_ptr.h"
- using namespace std;
- int main(){
- smart_ptr<person> p(new person("Cici"));
- p -> tell();
- {
- smart_ptr<person> q = p;
- q -> tell();
- }
- }
程序出错了。分析一下很容易找到原因:我们的智能指针q在程序跑出自己的作用域后就释放了指向的person对象,而我们的指针p由于和q指向的同一对象,所以在程序退出时企图再次释放一个已经被释放的对象,显然会出现经典的segmentation fault异常了。所以,我们“简单的”smart_ptr是过于“简单”了。
有什么办法解决这个问题呢?想想操作系统中的文件引用计数器。操作系统为每个打开的文件维护一个“引用计数”,当多个进程同时打开一个文件时系统会依次将引用计数加1。若某个进程关闭了某个文件此时文件不会立即被关闭,系统只是将引用计数减1,当引用计数为0时表示这时已经没用人使用这个文件了,系统才会把文件资源释放。所以这里,我们也可以为smart_ptr所指向的对象维护一个引用计数,当有新的smart_ptr指向这个对象时我们将引用计数加1,smart_ptr被释放时我们只是将引用计数减一;当引用计数减为0时,我们才真正的销毁smart_ptr所指向的对象。
另外,我们之前的smart_ptr还缺少无参构造函数,拷贝构造函数和对“=”运算符的重载。我们之前的版本并没有重载“=”运算符但程序依然可以正常执行,这是因为此时使用了编译器的合成版本,这也是不安全的(尽管这里没有发现问题)。
好吧,下面是最终的修改版本(添加的部分我特意都添加了注释):
- //
- //smart_ptr.h : our simple smart pointer
- //
- template <typename T>
- class smart_ptr{
- public:
- //add a default constructor
- smart_ptr();
- //
- smart_ptr(T* p);
- ~smart_ptr();
- T& operator*();
- T* operator->();
- //add assignment operator and copy constructor
- smart_ptr(const smart_ptr<T>& sp);
- smart_ptr<T>& operator=(const smart_ptr<T>& sp);
- //
- private:
- T* ptr;
- //add a pointer which points to our object's referenct counter
- int* ref_cnt;
- //
- };
- template <typename T>
- smart_ptr<T>::smart_ptr():ptr(0),ref_cnt(0){
- //create a ref_cnt here though we don't have any object to point to
- ref_cnt = new int(0);
- (*ref_cnt)++;
- }
- template <typename T>
- smart_ptr<T>::smart_ptr(T* p):ptr(p){
- //we create a reference counter in heap
- ref_cnt = new int(0);
- (*ref_cnt)++;
- }
- template <typename T>
- smart_ptr<T>::~smart_ptr(){
- //delete only if our ref count is 0
- if(--(*ref_cnt) == 0){
- delete ref_cnt;
- delete ptr;
- }
- }
- template <typename T>
- T& smart_ptr<T>::operator*(){
- return *ptr;
- }
- template <typename T>
- T* smart_ptr<T>::operator->(){
- return ptr;
- }
- template <typename T>
- smart_ptr<T>::smart_ptr(const smart_ptr<T>& sp):ptr(sp.ptr),ref_cnt(sp.ref_cnt){
- (*ref_cnt)++;
- }
- template <typename T>
- smart_ptr<T>& smart_ptr<T>::operator=(const smart_ptr<T>& sp){
- if(&sp != this){
- //we shouldn't forget to handle the ref_cnt our smart_ptr previously pointed to
- if(--(*ref_cnt) == 0){
- delete ref_cnt;
- delete ptr;
- }
- //copy the ptr and ref_cnt and increment the ref_cnt
- ptr = sp.ptr;
- ref_cnt = sp. ref_cnt;
- (*ref_cnt)++;
- }
- return *this;
- }
- #include "person.h"
- #include "smart_ptr.h"
- using namespace std;
- int main(){
- smart_ptr<person> r;
- smart_ptr<person> p(new person("Cici"));
- p -> tell();
- {
- smart_ptr<person> q = p;
- q -> tell();
- r = q;
- smart_ptr<person> s(r);
- s -> tell();
- }
- r -> tell();
- }
可以看到,我们的Cici对象只有在最后才被销毁,我们的smart_ptr终于顺利完成任务了。
ps:
写到这里,我们的smart_ptr是否完美了呢?No。
比如:我们的对象如果被多个线程中的smart_ptr引用,我们的smart_ptr就又有出问题的隐患了,因为这里根本没有考虑线程对ref_cnt访问的互斥,所以我们的引用计数是有可能出现计数问题的。这里就不再实现了,毕竟,我们这里的smart_ptr只是是“简单的”实现~
//main.cpp
#include "person.h"
#include "smart_ptr.h"
using namespace std;
int test() {
//auto_ptr<person> p(new person("Cici"));
//SmartPointer<person> p(new person("Cici"));
//p -> tell();
SmartPointer<person> r(new person("taoqi"));
SmartPointer<person> p(new person("Cici"));
p -> tell();
{
SmartPointer<person> q = p;
q -> tell();
r = q;
SmartPointer<person> s(r);
s -> tell();
}
r -> tell();
return 0;
}
int main(){
test();
return 0;
}
//smart_ptr.h
template <class T>
class SmartPointer {
public:
SmartPointer(T* ptr) {
ref = ptr;
ref_count = (unsigned*)malloc(sizeof(unsigned));
*ref_count = 1;
}
SmartPointer(SmartPointer<T> & sptr) {
ref = sptr.ref;
ref_count = sptr.ref_count;
++*ref_count;
}
T* SmartPointer<T>::operator->() {
return ref;
}
T& SmartPointer<T>::operator*() {
return *ref;
}
~SmartPointer() {
--*ref_count;
if (*ref_count == 0) {
delete ref;
free(ref_count);
ref = NULL;
ref_count = NULL;
}
}
SmartPointer<T> & operator=(SmartPointer<T> & sptr) {
if (this != &sptr) {
ref = sptr.ref;
ref_count = sptr.ref_count;
++*ref_count;
}
return *this;
}
T getValue() {
return *ref;
}
protected:
T * ref;
unsigned * ref_count;
};
基本的函数:构造函数、析构函数、拷贝构造函数
重载运算符的函数:T* operator->() T& operator*() SmartPointer<T> & operator=(SmartPointer<T>& sptr) T getValue()
计数器增加的场合:拷贝构造函数,重载=的操作
计数器减少的场合:析构函数,即离开作用域的场合
//
//a person who can tell us his/her name.
//
#include<iostream>
#include<string>
using namespace std;
class person{
public:
person(string);
void tell();
~person();
private:
string name;
};
person::person(string name):name(name){
}
void person::tell(){
cout << "Hi! I am " << name << endl;
}
person::~person(){
cout << "Bye!" << endl;
}