0.使用普通指针带来的问题

c++ 的内存管理是一个让人非常头疼的问题,当我们写一个new语句时,如果没有写响应的delete语句,就会造成内存泄漏的问题。或者就算我们写了delete语句,有可能程序还没有执行到delete语句就返回或者跳转到其他的语句,一样会造成内存泄漏。看下面这个例子

void test()
{
	double *p = new double;
	*p = 5.5;
	return;
}

执行这个函数,到第一条语句的时候,在栈内存中分配了一个4字节的内存存储指针p(假设我们的系统是32位的),在堆中申请了一个8字节的内存存储一个double类型。指针p指向double类型的首地址。执行到return的时候,栈中p占用的内存会被释放,但是堆中的内存不会被释放。这就造成了内存泄漏的问题。如果在函数末尾,p被释放的时候,p所指向的内存也能够被释放掉,那该有多好。但是,很可以,p只是换一个普通的指针,不是具有析构函数的对象。如果它是对象,则可以在对象过期的时候,调用析构函数删除它指向的内存。这个其实就是c++中智能指针干的事情。c++提供了三种方案来解决这个问题,auto_ptr、unique_ptr、shared_ptr。模板auto_ptr是c++98提供的方案,已经被c++11抛弃,并且提供了另外两种替代方案。

1. 使用智能指针

先来看一个例子,如何使用智能指针

#include<iostream>
#include<memory>
#include<string>
using namespace std;
class report {
    private:
        string str;
    public:
        report(const string s):str(s) {
            cout<<"create obj"<<endl;
        }
        ~report() {
            cout<<"destroy"<<endl;
        }
        void comment() const {
            cout<<str<<endl;
        }
};
int main()
{
    {
        auto_ptr<report> p(new report("auto_ptr"));
        p->comment();
    }
    {
        shared_ptr<report> p(new report("shared_ptr"));
        p->comment();
    }
    {
        unique_ptr<report> p(new report("unique_ptr"));
        p->comment();
    }
    return 0;
}

执行编译指令

g++ -g pointer.cpp  -std=c++11

运行结果如下:

create obj
auto_ptr
destroy
create obj
shared_ptr
destroy
create obj
unique_ptr
destroy

所有智能指针都有一个excplicit构造函数,这个构造函数将指针作为参数,自动将指针转换为智能指针类型。

template<class X>class auto_ptr {
    public:
        explicit auto_ptr(X* p = 0) throw();
        ···
};

下面几种赋值有的合法有的不合法

shared_ptr<double> pd;
double  *p = new double();
pd = p ;  	//不允许
pd = shared_prt<double>(p);//  显式转换,允许
shared_prt<double> q = p;  //不允许
share_ptr<double> q(p);    //允许

智能指针要注意的一点

string a("hello,world");
shared_ptr<string> p(&a);		//错误!!

p指针过期时,程序将delete运算符用于非堆内存,这是错误的。

2. auto_ptr、shared_ptr、unique_ptr的区别

  1. 一个auto_ptr赋值给另外一个auto_ptr,会转交所有权,同时自己变成空指针。对于某一个特定的对象,只有一个auto_ptr指针可以拥有它,这样,只有拥有这个对象的指针才能删除该对象。unique_ptr也是采取这种所有权转让的策略,只是更加严格。
auto_ptr<string> p, q;
p =auto_ptr(new string("test"));
q = p ;   // 允许,q拥有控制权,p变成空指针

例子

int main()
{

    auto_ptr<string> p[3] = 
    {
        auto_ptr<string>(new string("A")),
        auto_ptr<string>(new string("B")),
        auto_ptr<string>(new string("C"))
    };
    auto_ptr<string> ap;
    ap = p[2];         //ap拥有控制权,p[2]变成空指针
    for(int i = 0; i < 3; i++) {
        cout << *p[i]<<endl; 
    }
    cout<<"-----"<<endl;
    cout<<*ap<<endl;
    return 0;
}

程序输出结果:

A
B
Segmentation fault    //打印p[2]程序崩溃
  1. unique_ptr不允许将一个指针赋值给另外一个指针,除非是函数返回一个指针然后赋值给另外一个指针
unique_ptr<string> createstring() ;
 unique_ptr<string> p, q;
 p = unique_ptr(new string("zhengk"));
 p = q ;//不允许,编译不通过
 p = createstring();  //允许

例子

#include<iostream>
#include<memory>
#include<string>
using namespace std;
unique_ptr<string> createstring() 
{
    unique_ptr<string> p;
    p = unique_ptr<string>(new string("zhengkang"));
    return p;
}
int main()
{

    unique_ptr<string> p[3] = 
    {
        unique_ptr<string>(new string("A")),
        unique_ptr<string>(new string("B")),
        unique_ptr<string>(new string("C"))
    };
    unique_ptr<string> ap;
    for(int i = 0; i < 3; i++) {
        cout << *p[i]<<endl; 
    }
    cout<<"-----"<<endl;
    ap = createstring();
    cout<<*ap<<endl;
    return 0;
}

编译运行结果:

A
B
C
-----
zhengkang
  1. shared_ptr 采用引用计数策略,shared_ptr跟踪引用特定对象的智能指针数。赋值时,指针计数加1,指针过期时,计数减1。仅当最后一个指针过期时,调用delete。