Boost之內存管理學習(一)

說起內存管理,大多數人第一反應就是new,delete等操作,這一類操作所針對的對象就是堆對象,在使用這一類操作時,一個很突出的問題就是內存泄露,這也是每個C++程序猿的揮之不去的噩夢,曾幾何時因爲一個不起眼的指針釋放問題,而導致了服務器因內存寫滿而宕機,而如今這類問題,對於C++程序開發者而言已經不再是問題了,爲了解決這個問題,C++程序員採用了RAII機制(資源獲取即初始化),具體的思想就是:在對象初始化時分配資源,而在對象釋放時,則釋放資源。這類思想的最直接的實現方式就是採用智能指針,下面我們就來看看在boost中的一些比較常用的智能指針吧,在boost.smart_ptr庫中主要包括了六種智能指針,分別是:1.scoped_ptr,2.scoped_array,3.shared_ptr,4. shared_array,5.weak_ptr,6.intrusive_ptr,接下來我們就來看看這六種智能指針的是使用方法吧,在使用這類指針之前,我們需要包含的頭文件<boost/smart_ptr.hpp>

1. scoped_ptr智能指針

     scoped_ptr智能指針最大的特點就是對分配對象的所有權十分嚴格,並且不允許轉讓所有權,換句話說一旦scoped_ptr獲取了對象的管理權時,你就不能將該對象的管理權取回, 接下來看看scoped_ptr的類的聲明吧,代碼如下:

template<class T>
class scoped_ptr{
    public:
        explicit scoped_ptr(T* p = 0);
        ~scoped_ptr();
        void reset(T* p = 0);
        T& operator*() const;
        T* operator->() const;
        T* get() const;
        void swap(scoped_ptr& b);
    private:
        T* ptr;
        scoped_ptr(scoped_ptr const&);
        scoped_ptr& operator = (scoped_ptr const&);
};

從它的頭文件中,我們可以看出來,scoped_ptr類是不能被複制,從而保證了其所管理的對象不會轉讓給其他的指針,並且在類的 成員變量中,我們看到了被管理的原始對象指針,這個指針主要是在scoped_ptr構造函數中被初始化,並且在析構函數中被釋放,

scoped_ptr指針不支持比較操作,不能再scoped_ptr對象之間進行相等或不相等的比較操作,下面我們就來看看scoped_ptr智能指針的具體用法吧,代碼如下:

#include <boost/smart_ptr.hpp>
#include <iostream>
using namespace boost;
using namespace std;
struct posix_file
{
    posix_file(const char* file_name)
    {
        cout<<"open file:"<<file_name<<endl;
    }
    ~posix_file()
    {
        cout<<"close file"<<endl;
    }
    void display()
    {
        cout<<"display function"<<endl;
    }
};

int main()
{
    scoped_ptr<int> p(new int);
    if(p)
    {
        *p = 1000;
        cout<<*p <<endl;
    }
    p.reset();
    assert(p==0);
    scoped_ptr<posix_file> fp (new posix_file("tmp/a.txt"));

    fp->display();
    return 0;
}

測試結果:

1000
open file:tmp/a.txt
display function
close file

接下來,我們來看看scoped_ptr指針與auto_ptr指針的區別吧,其實scoped_ptr指針與auto_ptr指針最大的區別就是在對象的管理權上,scoped_ptr指針所管轄的對象管理權是不能轉讓的,而auto_ptr則可以轉讓。

2. scoped_array智能指針

    scoped_array智能指針與scoped_ptr指針的最大區別就是一個是管理對象數組,一個是管理單個對象,首先來看看scoped_array類的聲明吧,代碼如下:

template<class T>
class scoped_array
{
    public:
        explicit scoped_array(T* p = 0);
        ~scoped_array();

        void reset(T* p = 0);
        T& operator[](std::ptrdiff_t i) const;
        T* get() const;

        void swap(scoped_array& b);
};

scoped_array的主要特點:

1) 構造函數接受的指針必須是new[]的結果

2)沒有重載*,->

3)析構函數使用delete[]來釋放對象

4)提供operator[]操作符重載

5)沒有begin(),end()等類似容器的迭代器

scoped_array智能指針的基本用法與scoped_ptr類似,唯一不同的就是scoped_array包裝的是new[]產生的指針,而在析構時調用delete[],代碼如下:

    scoped_array<int> sa(new int[100]);
    fill_n(&sa[0],100,5);
    sa[10] = sa[20] + sa[30];

注意:在使用scoped_array指針時,需要特別注意operator[]操作,因爲scoped_array指針不提供數組索引範圍檢查,如果使用時,超過了動態數組大小的索引時或使用負數作爲索引時將引發未知行爲,另外針對一般的動態數組的需求,建議使用vector,而非scoped_array,一個很簡單的原因:scoped_array不支持動態擴展容器大小。

3. share_ptr智能指針

    這個指針想必大家都很熟悉了,這個也是boost.smart_ptr中最有價值的智能指針了,share_ptr智能指針是一種基於引用計數型的智能指針,與scoped_ptr最大的區別就是它可以被自由的拷貝和複製,其可以安全地放置在標準容器中,下面就來看看這個無比強大的基於引用計數型的智能指針的類聲明吧,代碼如下:

template<class T>
class share_ptr
{
    public:
        typedef T element_type;
        template<class Y> explicit share_ptr(Y* p);
        template<class Y,class D> share_ptt(Y* p, D d);
        ~share_ptr();

        share_ptr(share_ptr const& r);
        template<class Y> explicit share_ptr(std::auto_ptr<Y>& y);
        share_ptr& operator = (share_ptr const& r);
        template<class Y> share_ptr& operator = (share_ptr<Y> const& y);
        template<class Y> share_ptr& operator = (std::auto_ptr<Y>& y);

        void reset();
        template<class Y> void reset(Y* p);
        template<class Y,class D> void reset(Y* p,D d);

        T& operator*() const;
        T* operator->() const;
        T* get() const;

        bool unique() const;
        long use_count const;
        void swap(share_ptr& b);
};

 在share_ptr類中,其中有兩個成員函數是對引用計數進行操作的,第一個是unique,這個函數的主要用途就是用於判別引用計數是否爲1,而use_count這個函數則是用於測試,在share_ptr中還支持比較操作,不要是比較share_ptr所管理的對象是否一樣,此外不支持大於小於的比較,另外,在share_ptr指針轉換時,其提供了三個轉換函數:static_pointer_cast,const_pointer_cast 以及 dynamic_pointer_cast,其用法類似C++中的三個相似的轉換函數,只是其轉換後的類型是share_ptr。下面就來看看幾個簡單的應用,代碼如下:

class share_base
{
    public:
        share_base(){}
        share_base(shared_ptr<int> p_):p(p_){}
        void print()
        {
            cout <<"count:"<<p.use_count()<<"v="<<*p<<endl;
        }
    private:
        shared_ptr<int> p;
};

void print_func(shared_ptr<int>  p)
{
    cout<<"count:"<<p.use_count()<<"v="<<*p<<endl;
}

class share_sub : public share_base
{
    public:
        share_sub(shared_ptr<int> p_):share_base(p_){}
        void print()
        {
            cout<<"this is a sub class\n"<<endl;
        }

};

int main()
{
    shared_ptr<int> p(new int(100));
    share_base p1(p),p2(p);
    p1.print();
    p2.print();

    *p = 20;
    print_func(p);
    p1.print();

    shared_ptr<share_sub> sst(new share_sub(p));
    shared_ptr<share_base> sb  = static_pointer_cast<share_base>(sst);
    sb->print();
    return 0;
}

測試結果:

count:3v=100
count:3v=100
count:4v=20
count:3v=20
count:4v=20
從測試結果中可以看出,share_ptr引用計數的變化,p1,p2和p是共享一個原始對象,在print_func中由於傳參採用的是值傳遞的形式,引入了臨時變量,因此引用計數爲4,而離開了print_func,臨時對象被刪除了,所以引用計數又回到3,當使用static_pointer_cast時,調用了拷貝函數,從而使得引用計數增1。針對share_ptr的操作,我們再來看看幾個比較典型的應用吧,代碼如下:

1. 使用make_shared工廠函數

template<class T,class ... Args>
share_ptr<T> make_shared(Args && ... args);

實例代碼如下:

#include <boost/make_shared.hpp>
#include <iostream>
#include <vector>
using namespace boost;
using namespace std;

class Date
{
    public:
        Date()
        {
            year = month = day = 0;
        }
        Date(int year,int month, int day):year(year),month(month),day(day){}
        Date(const Date& test)
        {
            year = test.year;
            month = test.month;
            day = test.day;
        }
        Date& operator = (const Date& test)
        {
            if(this == &test)
                return *this;

            //Date(test);
            year = test.year;
            month = test.month;
            day = test.day;
            return *this;
        }

        void display()
        {
            cout<<"year:"<< year <<"month: "<< month <<"day: "<<day<<endl;
        }
    private:
        int year;
        int month;
        int day;
};
int main()
{
    shared_ptr<Date> dat = make_shared<Date>(2014,2,11);
    shared_ptr<vector<Date> > datvec = make_shared<vector<Date> >(10,Date(2014,2,11));
    dat->display();
    for(int i = 0;i<datvec->size();i++)
        (*datvec)[i].display();
    return 0;
}

運行結果:

year:2014month: 2day: 11
year:2014month: 2day: 11
year:2014month: 2day: 11
year:2014month: 2day: 11
year:2014month: 2day: 11
year:2014month: 2day: 11
year:2014month: 2day: 11
year:2014month: 2day: 11
year:2014month: 2day: 11
year:2014month: 2day: 11
year:2014month: 2day: 11
2. 橋接模式

    橋接模式是一種結構型設計模式,它的主要思想就是將具體的實現細節給隱藏掉,已到達類之間的最小的耦合關係,scoped_ptr和share_ptr都可以實現橋接模式,但是share_ptr相對來說更加合適,因爲它支持拷貝賦值操作,多說一點,橋接模式其實在實現開發中應用還是很廣泛的,因爲它最大的好處在前面已經提及到,隱藏實現,減少耦合,使得模塊之間,類之間的封裝性更好,下面就來看看如何用share_ptr來實現一個簡單的橋接模式吧,代碼如下:

#include <boost/make_shared.hpp>
#include <iostream>
#include <string>
using namespace std;
using namespace boost;

class bookImpl
{
    public:
        bookImpl(std::string bookname = std::string(),const int price = 0):bookname(bookname),price(price){}
        ~bookImpl(){}

        void display()
        {
            cout<<"bookname:"<<bookname<<" "<<"price:"<<price<<" "<<endl;
        }
    private:
        string bookname;
        int price;
};

class book
{
    friend class bookImpl;
    public:
        book():p(new bookImpl){}
        book(shared_ptr<bookImpl> const& p_):p(p_){}
        void print()
        {
            p->display();
        }
    private:
        shared_ptr<bookImpl> p;
};


int main()
{
    book b1;
    b1.print();

    shared_ptr<bookImpl> bl(new bookImpl("C++ design",20));
    book b2(bl);
    b2.print();
}

測試結果如下:

bookname: price:0
bookname:C++ design price:20
3. 應用工廠模式

   工廠模式是一種創建型設計模式,在上面的make_shared就是一個基於工廠模式的好例子,下面我們就來看看如何使用share_ptr來實現一個簡單的工廠,在使用share_ptr指針來實現工廠模式之前,我們需要明白share_ptr實現工廠模式的原理,主要是通過修改工廠方法的接口,使其返回share_ptr對象,而非原始對象指針,好了,說了這些,接着看代碼吧,代碼如下:

 

#include <boost/smart_ptr.hpp>
#include <iostream>
#include <string>
using namespace boost;
using namespace std;

class book
{
    public:
        virtual void displayName() = 0;
        virtual void displayPrice() = 0;
        virtual void setBookName(const std::string& str) = 0;
        virtual void setBookPrice(const int price) = 0;
    protected:
        virtual ~book(){}
};

class ComputerBook : public book
{
    public:
        ComputerBook(std::string bookname = std::string(),const int price = 0):bookname(bookname),price(price){}
        ~ComputerBook(){}

        void displayName()
        {
            cout<<"bookname:"<<bookname<<endl;
        }
        void displayPrice()
        {
            cout<<"price:"<<price<<endl;
        }
        void setBookName(const std::string& bookname)
        {
            this->bookname = bookname;
        }
        void setBookPrice(const int price)
        {
            this->price = price;
        }
    private:
        std::string bookname;
        int price;
};
template<class T,class S,class H,class B>
shared_ptr<T> create(H l,B t)
{
    return shared_ptr<S>(new S(l,t));
}

template<class T,class S>
shared_ptr<T> create()
{
    return shared_ptr<S>(new S());
}

int main()
{
    shared_ptr<book> p = create<book,ComputerBook,std::string,int>("C++ Design",10);
    p->displayName();
    p->displayPrice();

    shared_ptr<book> p1 = create<book,ComputerBook>();
    p1->displayName();
    p1->displayPrice();

    p1->setBookName("AddressBook");
    p1->setBookPrice(20);
    p1->displayName();
    p1->displayPrice();
    return 0;
}

測試結果:

bookname:C++ Design
price:10
bookname:
price:0
bookname:AddressBook
price:20

4. share_array智能指針

   share_array類似share_ptr,它們之間不同的是:share_ptr封裝的是new操作,而share_array封裝的是new[]操作,下面我們來看看share_array的類聲明吧,代碼如下:

template<class T>
class share_array
{
    public:
        explicit share_array(T* p = 0);
        template<class D> share_array(T* p,D d);
        ~share_array();

        share_array(share_array const& r);
        share_array& operator = (share_array const& r);

        void reset(T* p = 0);
        template<class D> void reset(T* p,D d);

        T& operator[](std::ptrdiff_t) const() const;
        T* get() const;

        bool unquie() const;
        long use_count() const;
        void swap(share_array<T>& b);
        ...
}

接下來,我們就來看看share_array指針的基本用法吧,代碼如下:

#include<boost/smart_ptr.hpp>
using namespace boost;
int main()
{
       share_array<int> p (new int[100]);
       share_array<int> p1 = p;
       p[0] = 1000;
       assert(p1[0] == 1000);
}

share_array使用起來很方便,但是也需要注意一點:share_array在使用operator[]時,是沒有提供索引範圍檢查的,如果索引超過了動態數組的大小的話,將會發生不可預知的行爲。

5. weak_ptr智能指針

     這個智能指針主要是爲了配合share_ptr而引入的,它不具有普通指針的行爲,並且沒有重載operator*和->,它的最大的作用就是協助share_ptr,下面就來看看weak_ptr類聲明吧,代碼如下:

template<class T>
class weak_ptr
{
    public:
        explicit weak_ptr();
        template<class Y> weak_ptr(share_ptr<Y>const& r);
        weak_ptr(weak_ptr const& r);

        ~weak_ptr();
        weak_ptr& operator = (weak_ptr const& r);
        long use_count() const;
        bool expired() const;
        share_ptr<T> lock() const;

        void reset();
        void swap(weak_ptr<T>& b);
        ...
};

weak_ptr智能指針最大的特點就是:它只能獲得對象的觀測權,而不能獲得對象控制權,換句話說,weak_ptr指針的構造不能引起指針引用計數器增加,同樣的在析構時,也無法導致計數器減少,下面就從代碼中看看weak_ptr的特性吧,代碼如下:

#include <boost/smart_ptr.hpp>
#include <iostream>
using namespace boost;
using namespace std;

int main()
{
    shared_ptr<int> p (new int(100));
    assert(p.use_count() == 1);
    weak_ptr<int> w(p);
    assert(p.use_count() == 1);

    if(!w.expired())
    {
        shared_ptr<int> r2 = w.lock();
        assert(p.use_count()==2);
    }
    assert(p.use_count() == 1);
    p.reset();
    assert(w.expired());
    assert(!w.lock());
    return 0;
}

在結束本篇博文之前,我們再來看看weak_ptr的一個小應用吧,這個應用就是通過this指針來獲得自己的share_ptr指針,即使得對象自己管理自己,而weak_ptr指針則是通過觀測this指針,其不會增加引用計數,在需要的時候,可以通過lock函數來返回一個符合要求的share_ptr供外界使用,代碼如下:

 

#include <boost/smart_ptr.hpp>
#include <boost/enable_shared_from_this.hpp>
#include <iostream>
using namespace boost;
using namespace std;


class self_shared: public enable_shared_from_this<self_shared>
{
    public:
        self_shared(int x):n(x){}
        void print()
        {
            cout<<"self_shard:"<<n<<endl;
        }
        void setValue(const int x)
        {
            n = x;
        }
    private:
        int n;
};
int main()
{
    shared_ptr<self_shared> sp = make_shared<self_shared>(1000);
    sp->print();

    shared_ptr<self_shared> p = sp->shared_from_this();
    p->setValue(100);
    p->print();
    return 0;
}

測試結果

self_shard:1000
self_shard:100

總結

       本篇主要是分析了下boost庫裏的一些內存管理方面的東西,內容比較基礎,在學習這方面的東西時,自己也試着實現了下其中的幾個智能指針,有時間的話,將代碼貼出來,在下篇博文中,我們來分析下boost中的pool這個庫,這個庫也是異常的強大,盡情期待吧,好了,今天的博文到此結束了,謝謝

如果需要,請註明轉載,多謝

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章