青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品

Design&Art

C++博客 首頁(yè) 新隨筆 聯(lián)系 聚合 管理
  26 Posts :: 0 Stories :: 38 Comments :: 0 Trackbacks

智能指針的標(biāo)準(zhǔn)之爭(zhēng):Boost vs. Loki

撰文/馬維達(dá)

 2001 年10 月和2002 年4 月,在美國(guó)的華盛頓和荷蘭的安的列斯群島上分別召開了兩次C++標(biāo)準(zhǔn)會(huì)議。會(huì)議的內(nèi)容之一是對(duì)一項(xiàng)新的C++特性提議——智能指針(Smart Pointer)——進(jìn)行討論。本文將對(duì)可能成為C++新標(biāo)準(zhǔn)的兩種智能指針?lè)桨福˙oost vs. Loki)進(jìn)行介紹和分析,并給出了相應(yīng)的使用實(shí)例。

關(guān)鍵詞:智能指針 C++ Boost Loki

  在現(xiàn)在的標(biāo)準(zhǔn)C++中,只有一種智能指針:std::auto_ptr。其原因并非是因?yàn)閍uto_ptr 已足以應(yīng)付所有相關(guān)的工作——實(shí)際上,auto_ptr 有一個(gè)重大的缺陷,就是它不能被用在STL 容器中——而是因?yàn)楝F(xiàn)在的C++標(biāo)準(zhǔn)在制定時(shí)并未能對(duì)智能指針進(jìn)行全面的考察。按照C++標(biāo)準(zhǔn)委員會(huì)成員Herb Sutter 的說(shuō)法,只有一種標(biāo)準(zhǔn)的智能指針是一件“可羞”的事情:首先,智能指針?biāo)茏龅脑S多有用的事情,是可憐的auto_ptr 不能完成的;其次,在有些情況下使用auto_ptr 可能會(huì)造成問(wèn)題,上面所說(shuō)的不能在容器中使用就是一例。實(shí)際上,許多程序員已經(jīng)開發(fā)了各種有用的智能指針,有些甚至在auto_ptr 被定為標(biāo)準(zhǔn)之前就已存在,但問(wèn)題是,它們不是標(biāo)準(zhǔn)的。在這樣的情況下,C++標(biāo)準(zhǔn)委員會(huì)考慮引入新的智能指針,也就是自然而然的事情了。目前進(jìn)入委員會(huì)視野的,主要有兩種智能指針?lè)桨福築oost 智能指針和Loki 智能指針。前者是由C++標(biāo)準(zhǔn)委員會(huì)庫(kù)工作組發(fā)起的Boost 組織開發(fā)的,而后者由世界級(jí)的C++專家Andrei Alexandrescu 開發(fā),并在他所著的“Modern C++ Design”一書中進(jìn)行了詳細(xì)的闡釋。下面,讓我們分別來(lái)看一看這兩種方案各自的技術(shù)特點(diǎn)。


一、 Boost 智能指針
Boost 的智能指針?lè)桨笇?shí)現(xiàn)了五種智能指針模板類,每種智能指針都用于不同的目的。這五種智能指針是:

  template<typename T> class scoped_ptr;
  template<typename T> class scoped_array;
  template<typename T> class shared_ptr;
  template<typename T> class shared_array;
  template<typename T> class weak_ptr;


下面將分別介紹它們各自的特性,并給出相應(yīng)的使用實(shí)例:


  scoped_ptr:意在用作指向自動(dòng)(棧)對(duì)象的、不可復(fù)制的智能指針。該模板類存儲(chǔ)的是指向動(dòng)態(tài)分配的對(duì)象(通過(guò)new 分配)的指針。被指向的對(duì)象保證會(huì)被刪除,或是在scoped_ptr 析構(gòu)時(shí),或是通過(guò)顯式地調(diào)用reset 方法。注意該模板沒(méi)有“共享所有權(quán)”或是“所有權(quán)轉(zhuǎn)讓”語(yǔ)義。同時(shí),它也是不可復(fù)制的(noncopyable)。正因?yàn)槿绱耍谟糜诓粦?yīng)被復(fù)制的指針時(shí),它比shared_ptr 或std:auto_ptr 要更安全。與auto_ptr一樣,scoped_ptr 也不能用于STL 容器中;要滿足這樣的需求,應(yīng)該使用shared_ptr。另外,它也不能用于存儲(chǔ)指向動(dòng)態(tài)分配的數(shù)組的指針,這樣的情況應(yīng)使用scoped_array。

下面是使用scoped_ptr 的一個(gè)簡(jiǎn)單實(shí)例:

class CTest
{
public:
    CTest() : m_id(0) {}
    CTest(int id) : m_id(id) {}
    ~CTest() { std::cout << "id: " << m_id << " - Destructor is being called\n"; }
    void SetId(int id) { m_id = id; }
    int GetId() { return m_id; }
    void DoSomething()
    { std::cout << "id: " << m_id << " - Doing something\n"; }
private:
    int m_id;
};
void main()
{
    boost::scoped_ptr<CTest> pTest(new CTest);
    pTest->DoSomething();
}


其運(yùn)行結(jié)果為:
  id: 0 - Doing something
  id: 0 - Destructor is being called
(以下的幾個(gè)例子所用的CTest 類的定義完全相同,為節(jié)省篇幅,不再列出——作者)

顯然,盡管我們自己沒(méi)有調(diào)用delete,pTest 仍然為我們正確地刪除了它所指向的對(duì)象。看起來(lái)scoped_ptr的用途和auto_ptr 十分類似,但實(shí)際上,scoped_ptr 類型的指針的所有權(quán)不可轉(zhuǎn)讓,這一點(diǎn)是和auto_ptr相當(dāng)不同的。


  scoped_array:該模板類與scoped_ptr 類似,但意在用于數(shù)組而不是單個(gè)對(duì)象。std::vector 可用于替換scoped_array,并且遠(yuǎn)為靈活,但其效率要低一點(diǎn)。在不使用動(dòng)態(tài)分配時(shí),boost::array 也可用于替換scoped_array。

下面是一個(gè)使用scoped_array 的實(shí)例:

void main()
{
    boost::scoped_array<CTest> pTest(new CTest[2]);
    pTest[0].SetId(0);
    pTest[1].SetId(1);
    pTest[0].DoSomething();
    pTest[1].DoSomething();
   
std::cout << '\n';
}


其運(yùn)行結(jié)果為:
  id: 0 - Doing something
  id: 1 - Doing something
  id: 1 - Destructor is being called
  id: 0 - Destructor is being called
scoped_array 將負(fù)責(zé)使用delete [],而不是delete 來(lái)刪除它所指向的對(duì)象。


  shared_ptr:意在用于對(duì)被指向?qū)ο蟮乃袡?quán)進(jìn)行共享。與scoped_ptr 一樣,被指向?qū)ο笠脖WC會(huì)被刪除,但不同的是,這將發(fā)生在最后一個(gè)指向它的shared_ptr 被銷毀時(shí),或是調(diào)用reset 方法時(shí)。shared_ptr符合C++標(biāo)準(zhǔn)庫(kù)的“可復(fù)制構(gòu)造”(CopyConstructible)和“可賦值”(Assignable)要求,所以可被用于標(biāo)
準(zhǔn)的庫(kù)容器中。另外它還提供了比較操作符,所以可與標(biāo)準(zhǔn)庫(kù)的關(guān)聯(lián)容器一起工作。shared_ptr 不能用于存儲(chǔ)指向動(dòng)態(tài)分配的數(shù)組的指針,這樣的情況應(yīng)該使用shared_array。該模板的實(shí)現(xiàn)采用了引用計(jì)數(shù)技術(shù),所以無(wú)法正確處理循環(huán)引用的情況。可以使用weak_ptr 來(lái)“打破循環(huán)”。shared_ptr 還可在多線程環(huán)境中使用。

下面的例子演示怎樣將shared_ptr 用于std::vector 中:

typedef boost::shared_ptr<CTest> TestPtr;
void PT(const TestPtr &t)
{
    std::cout << "id: " << t->GetId() << "\t\t" << "use count: " << t.use_count() << '\n';
}
void main()
{
    std::vector<TestPtr> TestVector;
    TestPtr pTest0(new CTest(0));
    TestVector.push_back(pTest0);
    TestPtr pTest1(new CTest(1));
    TestVector.push_back(pTest1);
    TestPtr pTest2(new CTest(2));
    TestVector.push_back(pTest2);
    std::for_each(TestVector.begin(), TestVector.end(), PT);
    std::cout << '\n';
    pTest0.reset();
    pTest1.reset();
    pTest2.reset();
    std::for_each(TestVector.begin(), TestVector.end(), PT);
    std::cout << '\n';
    TestVector.clear();
    std::cout << '\n';
    std::cout << "exiting...\n";
}


其運(yùn)行結(jié)果為:
  id: 0 use count: 2
  id: 1 use count: 2
  id: 2 use count: 2
  id: 0 use count: 1
  id: 1 use count: 1
  id: 2 use count: 1
  id: 0 - Destructor is being called
  id: 1 - Destructor is being called
  id: 2 - Destructor is being called
  exiting...

運(yùn)行結(jié)果中的“use count”是通過(guò)shared_ptr 的use_count()方法獲得的“使用計(jì)數(shù)”,也就是,對(duì)所存儲(chǔ)指針進(jìn)行共享的shared_ptr 對(duì)象的數(shù)目。我們可以看到,在通過(guò)new 分配了3 個(gè)CTest 對(duì)象,并將相應(yīng)的shared_ptr 對(duì)象放入TestVector 后,三個(gè)使用計(jì)數(shù)都為2;而在我們使用reset()方法復(fù)位pTest0、pTest1 和pTest2 后,TestVector 中的各個(gè)shared_ptr 對(duì)象的使用計(jì)數(shù)變成了1。這時(shí),我們調(diào)用TestVector的clear()方法清除它所包含的shared_ptr 對(duì)象;因?yàn)橐呀?jīng)沒(méi)有shared_ptr 對(duì)象再指向我們先前分配的3個(gè)CTest 對(duì)象,這3 個(gè)對(duì)象也隨之被刪除,并導(dǎo)致相應(yīng)的析構(gòu)器被調(diào)用。


  shared_array:該模板類與shared_ptr 類似,但意在用于數(shù)組而不是單個(gè)對(duì)象。指向std::vector 的shared_ptr 可用于替換scoped_array,并且遠(yuǎn)為靈活,但其效率也要低一點(diǎn)。

下面是使用實(shí)例:

void main()
{
    boost::shared_array<CTest> pTest1(new CTest[2]);
    pTest1[0].SetId(0);
    pTest1[1].SetId(1);
    std::cout << "use count: " << pTest1.use_count() << "\n\n";
    boost::shared_array<CTest> pTest2(pTest1);
    std::cout << "use count: " << pTest1.use_count() << "\n\n";
    pTest1.reset();
    pTest2[0].DoSomething();
    pTest2[1].DoSomething();
    std::cout << '\n';
    std::cout << "use count: " << pTest1.use_count() << "\n\n";
}


其運(yùn)行結(jié)果為:
  use count: 1
  use count: 2
  id: 0 - Doing something
  id: 1 - Doing something
  use count: 1
  id: 1 - Destructor is being called
  id: 0 - Destructor is being called
如此例所示,我們通過(guò)new 所分配的數(shù)組只有在指向它的pTest1 和pTest2 都被銷毀或復(fù)位后才被刪除。


  weak_ptr:該模板類存儲(chǔ)“已由shared_ptr 管理的對(duì)象”的“弱引用”。要訪問(wèn)weak_ptr 所指向的對(duì)象,可以使用shared_ptr 構(gòu)造器或make_shared 函數(shù)來(lái)將weak_ptr 轉(zhuǎn)換為shared_ptr。指向被管理對(duì)象的最后一個(gè)shared_ptr 被銷毀時(shí)將刪除該對(duì)象,即使仍有weak_ptr 指向它也是如此。與原始指針不同的是,屆時(shí)最后一個(gè)shared_ptr 會(huì)檢查是否有weak_ptr 指向該對(duì)象,如果有的話就將這些weak_ptr 置為空。這樣就不會(huì)發(fā)生使用原始指針時(shí)可能出現(xiàn)的“懸吊指針”(dangling pointer)情況,從而獲得更高的安全水平。
  weak_ptr 符合C++標(biāo)準(zhǔn)庫(kù)的“可復(fù)制構(gòu)造”(CopyConstructible)和“可賦值”(Assignable)要求,所以可被用于標(biāo)準(zhǔn)的庫(kù)容器中。另外它還提供了比較操作符,所以可與標(biāo)準(zhǔn)庫(kù)的關(guān)聯(lián)容器一起工作。

void main()
{
    boost::shared_ptr<CTest> pTest(new CTest);
    boost::weak_ptr<CTest> pTest2(pTest);
    if(boost::shared_ptr<CTest> pTest3 = boost::make_shared(pTest2))
        pTest3->DoSomething();
    pTest.reset();
    assert(pTest2.get() == NULL);
}


其運(yùn)行結(jié)果為:
  id: 0 - Doing something
  id: 0 - Destructor is being called
main 函數(shù)最后的斷言確認(rèn)了pTest2 所存儲(chǔ)的指針的確已被置為NULL。

  顯然,Boost 的智能指針?lè)桨笗?huì)讓我們產(chǎn)生這樣的疑問(wèn):如果我們還需要其他類型的智能指針(比如支持COM 的智能指針),是否意味著我們必須在C++中再增加智能指針類型,或是采用非標(biāo)準(zhǔn)的實(shí)現(xiàn)呢?在泛型技術(shù)已得到極大發(fā)展的今天,Boost 的“增加增加再增加”的思路是不能讓人滿意的。正是在這里,我們看到了下面將要介紹的Loki Smart Pointer 的關(guān)鍵點(diǎn):通過(guò)基于策略(policy-based)的設(shè)計(jì)來(lái)實(shí)現(xiàn)通用的智能指針模板。


二、 Loki 智能指針
  按照美國(guó)傳統(tǒng)辭典(雙解)的解釋,Loki 是“A Norse god who created discord, especially among his fellow gods.”(斯堪的納維亞的一個(gè)制造混亂的神,尤其是在其同類之間)。就其給Boost 智能指針帶來(lái)的麻煩而言,Loki 智能指針倒真的當(dāng)?shù)闷疬@個(gè)名字;而在另一方面,就其實(shí)現(xiàn)的優(yōu)雅以及功能的強(qiáng)大(也就是說(shuō),它給開發(fā)者帶來(lái)的好處)而言,它也的確屬于“神族”。
  上面已經(jīng)說(shuō)過(guò),Loki 的智能指針?lè)桨覆捎昧嘶诓呗缘脑O(shè)計(jì)。其要點(diǎn)在于把將各功能域分解為獨(dú)立的、由主模板類進(jìn)行混合和搭配的策略。讓我們先來(lái)看一看Loki 智能指針模板類SmartPtr 的定義:

template
<
    typename T,
    template <class>class OwnershipPolicy =RefCounted,
    class ConversionPolicy =DisallowConversion,
    template <class>class CheckingPolicy =AssertCheck,
    template <class>class StoragePolicy =DefaultSPStorage
>
class SmartPtr;


我們可以看到,除了SmartPtr 所指向的對(duì)象類型T 以外,在模板類SmartPtr 中包括了這樣一些策略:OwnershipPolicy(所有權(quán)策略)、ConversionPolicy(類型轉(zhuǎn)換策略)、CheckingPolicy(檢查策略)、StoragePolicy(存儲(chǔ)策略)。正是通過(guò)這樣的分解,使得SmartPtr 具備了極大的靈活性。我們可以任意組合各種不同的策略,從而獲得不同的智能指針實(shí)現(xiàn)。下面先對(duì)各個(gè)策略逐一進(jìn)行介紹:

  OwnershipPolicy:指定所有權(quán)管理策略,可以從以下預(yù)定義的策略中選擇:DeepCopy(深度復(fù)制)、RefCounted(引用計(jì)數(shù))、RefCountedMT(多線程化引用計(jì)數(shù))、COMRefCounted(COM 引用計(jì)數(shù))、RefLinked(引用鏈接)、DestructiveCopy(銷毀式復(fù)制),以及NoCopy(無(wú)復(fù)制)。
  ConversionPolicy:指定是否允許進(jìn)行向被指向類型的隱式轉(zhuǎn)換。可以使用的實(shí)現(xiàn)有AllowConversion 和DisallowConversion。
  CheckingPolicy:定義錯(cuò)誤檢查策略。可以使用AssertCheck、AssertCheckStrict、RejectNullStatic、RejectNull、RejectNullStrict,以及NoCheck。
  StoragePolicy:定義怎樣存儲(chǔ)和訪問(wèn)被指向?qū)ο蟆oki 已定義的策略有:DefaultSPStorage、ArrayStorage、LockedStorage,以及HeapStorage。

除了Loki 已經(jīng)定義的策略,你還可以自行定義策略。實(shí)際上,Loki 的智能指針模板覆蓋了四種基本的Boost 智能指針類型:scoped_ptr、scoped_array、shared_ptr 和shared_array;至于weak_ptr,也可以通過(guò)定義相應(yīng)的策略來(lái)實(shí)現(xiàn)其等價(jià)物。通過(guò)即將成為C++標(biāo)準(zhǔn)(C++0x)的typedef 模板特性,我們還可以利用Loki 的SmartPtr 模板來(lái)直接定義前面提到的Boost 的前四種智能指針類型。舉例來(lái)說(shuō),我們可以這樣定義:

shared_ptr:
template<typename T> // typedef 模板還不是標(biāo)準(zhǔn)的
typedef Loki::SmartPtr
<
    T,
    RefCounted, // 以下都是缺省的模板參數(shù)
    DisallowConversion,
    AssertCheck,
    DefaultSPStorage
>
shared_ptr;


下面是一個(gè)使用Loki “shared_ptr”的實(shí)例:

typedef Loki::SmartPtr<CTest> TestPtr;
void PT(const TestPtr &t)
{
    std::cout << "id: " << t->GetId() << '\n';
}

void main()
{
    std::vector<TestPtr> TestVector;
    TestPtr pTest0(new CTest(0));
    TestVector.push_back(pTest0);
    TestPtr pTest1(new CTest(1));
    TestVector.push_back(pTest1);
    std::for_each(TestVector.begin(), TestVector.end(), PT);
    std::cout << '\n';
    Loki::Reset(pTest0, NULL);
    Loki::Reset(pTest1, NULL);
    std::for_each(TestVector.begin(), TestVector.end(), PT);
    std::cout << '\n';
    TestVector.clear();
    std::cout << '\n';
    std::cout << "exiting...\n";
}


其運(yùn)行結(jié)果為:
  id: 0
  id: 1
  id: 0
  id: 1
  id: 0 - Destructor is being called
  id: 1 - Destructor is being called
  exiting...

前面已經(jīng)提到,要通過(guò)Loki 定義與Boost 的shared_ptr 功能等價(jià)的智能指針,除了第一個(gè)模板參數(shù)以外,其他的參數(shù)都可以使用缺省值,所以在上面的例子中,我們直接使用“typedef Loki::SmartPtr<CTest> TestPtr;”就可以了。非常的簡(jiǎn)單!

為了進(jìn)一步說(shuō)明Loki 的“基于策略的設(shè)計(jì)方法”,讓我們?cè)賮?lái)看一個(gè)更為復(fù)雜的例子:通過(guò)Loki::SmartPtr 實(shí)現(xiàn)線程專有存儲(chǔ)(Thread-Specific Storage,TSS;又稱線程局部存儲(chǔ),Thread Local Storage,TLS)。

所謂線程專有存儲(chǔ),是指這樣一種機(jī)制,通過(guò)它,多線程程序可以使用一個(gè)邏輯上的全局訪問(wèn)點(diǎn)來(lái)訪問(wèn)線程專有的數(shù)據(jù),并且不會(huì)給每次訪問(wèn)增加額外的鎖定開銷。舉一個(gè)簡(jiǎn)單的例子,在C 語(yǔ)言中,我們可以通過(guò)errno變量來(lái)獲取錯(cuò)誤代碼;通常errno 就是一個(gè)普通的全局變量——在單線程環(huán)境中,這當(dāng)然沒(méi)有什么問(wèn)題,但如果
是多線程環(huán)境,這個(gè)全局的errno 變量就會(huì)給我們帶來(lái)麻煩了。TSS 正是解決這一問(wèn)題的有效方案。

顯然,智能指針的語(yǔ)義能夠很好地適用于TSS。我們可以編寫一種智能指針,使得所有對(duì)其所指向?qū)ο蟮脑L問(wèn)都成為線程專有的——也就是說(shuō),每個(gè)線程訪問(wèn)的實(shí)際上是自己專有的對(duì)象,但從程序的外表來(lái)看,卻都是對(duì)同一對(duì)象的訪問(wèn)。有了Loki::SmartPtr,我們可以非常容易地實(shí)現(xiàn)這樣的智能指針:如其名字所指示的,TSS 涉及的是存儲(chǔ)問(wèn)題,我們只要對(duì)Loki::SmartPtr 的StoragePolicy 進(jìn)行定制就可以了,其他的工作可以交給Loki::SmartPtr 去完成。

在POSIX PThreads 庫(kù)和Win32 中都提供了用于線程專有存儲(chǔ)的函數(shù),它們分別是pthread_key_create、pthread_setspecific、pthread_getspecific 和pthread_key_delete(POSIX PThreads 庫(kù)),以及TlsAlloc、TlsSetvalue、TlsGetvalue 和TlsFree(Win32)。關(guān)于這些函數(shù)的詳細(xì)信息,請(qǐng)參閱相關(guān)的文檔。

下面給出在MSVC 6.0 下實(shí)現(xiàn)的用于TSS 的StoragePolicy,并通過(guò)注釋逐行進(jìn)行分析(這個(gè)實(shí)現(xiàn)使用了PThreads-Win32 庫(kù),這是一個(gè)Win32 上的PThreads 實(shí)現(xiàn)。使用Win32 的線程專有函數(shù)也可以實(shí)現(xiàn)類似的StoragePolicy,但編寫在線程退出時(shí)調(diào)用的CleanupHook()卻需要一點(diǎn)“竅門”。具體方法可參考Boost 的thread_specific_ptr 實(shí)現(xiàn)):

namespace Loki
{
    // 實(shí)現(xiàn)TSS 的Loki 存儲(chǔ)策略。改編自Douglas C. Schmidt、Timothy H. Harrison
    // 和Nat Pryce 的論文“Thread-Specific Storage for C/C++”中的部分代碼。
    // 使用了“Loki VC 6.0 Port”。
template <class T> class TS_SPStorage
{
public:
    typedef T* StoredType; // 被指向?qū)ο蟮念愋汀?br>    typedef T* PointerType; // operator->所返回的類型。
    typedef T& ReferenceType; // operator*所返回的類型。
public:
    // 構(gòu)造器。對(duì)成員變量進(jìn)行初始化。
    TS_SPStorage() : once_(0), key_(0), keylock_(NULL)
        { pthread_mutex_init(&keylock_, NULL); }
    // 析構(gòu)器。釋放先前獲取的資源。
    ~TS_SPStorage()
    {
        pthread_mutex_destroy(&keylock_);
        pthread_key_delete(key_);
    }
    // 返回線程專有的被指向?qū)ο蟆?br>    PointerType operator->() const { return GetPointee(); }
    // 返回線程專有的被指向?qū)ο蟮囊谩?br>    ReferenceType operator*() { return *GetPointee(); }
    // Accessors。獲取線程專有的被指向?qū)ο蟆?br>    friend inline PointerType GetImpl(TS_SPStorage& sp)
    { return sp.GetPointee(); }
    // 獲取線程專有的被指向?qū)ο蟮囊谩?br>    // 該函數(shù)沒(méi)有實(shí)現(xiàn)。但NoCheck 需要它才能正常工作。
    friend inline const StoredType& GetImplRef(const TS_SPStorage& sp)
    { return 0; }
protected:
    // 銷毀所存儲(chǔ)的數(shù)據(jù)。空函數(shù)。該工作將由CleanupHook()在各個(gè)線程退出時(shí)完成。
    void Destroy() {}
    // 獲取當(dāng)前線程專有的數(shù)據(jù)。
    PointerType GetPointee()
    {
        PointerType tss_data = NULL;
        // 使用雙重檢查鎖定模式來(lái)在除了初始化以外的情況下避免鎖定。
        // 之所以在這里,而不是在構(gòu)造器中對(duì)“專有鑰”進(jìn)行初始化及分配TS 對(duì)象,
        // 是因?yàn)椋?1) 最初創(chuàng)建TS 對(duì)象的線程(例如,主線程)常常并不是使用
        // 它的線程(工作線程),所以在構(gòu)造器中分配一個(gè)TS 對(duì)象的實(shí)例常常并無(wú)
        // 好處,因?yàn)檫@個(gè)實(shí)例只能在主線程中訪問(wèn)。(2)在有些平臺(tái)上,“專有
        // 鑰”是有限的資源,所以等到對(duì)TS 對(duì)象進(jìn)行第一次訪問(wèn)時(shí)再進(jìn)行分配,有
        // 助于節(jié)省資源。
        // 第一次檢查。
        if(once_ == 0)
        {
            // 加鎖。確保訪問(wèn)的序列化。
            pthread_mutex_lock(&keylock_);
            // 雙重檢查。
            if(once_ == 0)
            {
                // 創(chuàng)建“專有鑰”。
                pthread_key_create(&key_, &CleanupHook);
                // 必須在創(chuàng)建過(guò)程的最后出現(xiàn),這樣才能防止其他線程在“專有鑰”
                // 被創(chuàng)建之前使用它。
                once_ = 1;
            }
            // 解鎖。
            pthread_mutex_unlock(&keylock_);
        }
        // 從系統(tǒng)的線程專有存儲(chǔ)中獲取數(shù)據(jù)。注意這里不需要加鎖。
        tss_data = (PointerType)pthread_getspecific(key_);
        // 檢查是否這是當(dāng)前線程第一次進(jìn)行訪問(wèn)。
        if (tss_data == NULL)
        {
            // 從堆中為TS 對(duì)象分配內(nèi)存。
            tss_data = new T;
            // 將其指針存儲(chǔ)在系統(tǒng)的線程專有存儲(chǔ)中。
            pthread_setspecific(key_, (void *)tss_data);
        }
        return tss_data;
    }
private:
    // 用于線程專有數(shù)據(jù)的“專有鑰”。
    pthread_key_t key_;
    // “第一次進(jìn)入”標(biāo)志。
    int once_;
    // 用于避免在初始化過(guò)程中產(chǎn)生競(jìng)態(tài)情況。
    pthread_mutex_t keylock_;
    // 清掃掛鉤函數(shù),釋放為TS 對(duì)象分配的內(nèi)存。在每個(gè)線程退出時(shí)被調(diào)用。
    static void CleanupHook (void *ptr)
    {
        // 這里必須進(jìn)行類型轉(zhuǎn)換,相應(yīng)的析構(gòu)器才會(huì)被調(diào)用(如果有的話)
        delete (PointerType)ptr;
    }
};

// 用于模擬typedef template 的結(jié)構(gòu)。
// 參見Herb Sutter 的“Template Typedef”一文。
struct TS_SPStorageWrapper
{
    template <class T>
    struct In
    {
        typedef TS_SPStorage<T> type;
    };
};
};


下面讓我們來(lái)看一個(gè)使用實(shí)例。首先讓我們先定義:

Loki::SmartPtr
<
    int,
    Loki::NoCopyWrapper,
    Loki::DisallowConversion,
    Loki::NoCheckWrapper,
    Loki::TS_SPStorageWrapper
> value;


其含義為:定義一種智能指針,被它指向的類型是int,OwnershipPolicy 是NoCopy,ConversionPolicy是DisallowConversion,CheckingPolicy 是NoCheck(因?yàn)門S 對(duì)象存儲(chǔ)方式的限制,這是惟一能和TS_SPStorage 一起使用的CheckingPolicy。讀者可自行嘗試找到更好的解決方案),StoragePolicy 是TS_SPStorage。
然后,編寫這樣一個(gè)程序:

pthread_mutex_t io_mutex = NULL; // iostreams 不一定是線程安全的!
void *thread_proc(void *param)
{
    int id = (int)param;
    *value = 0;
    for (int i = 0; i < 3; i++)
    {
        (*value)++;
        pthread_mutex_lock(&io_mutex);
        std::cout << "thread " << id << ": " << *value << '\n';
        pthread_mutex_unlock(&io_mutex);
    }
    return NULL;
}

void main(int argc, char* argv[])
{
    pthread_mutex_init(&io_mutex, NULL);
    pthread_t id[3];
    for(int i = 0; i < 3; i++)
        pthread_create(&id[i], 0, thread_proc, (void *)i);
    for(int i = 0; i < 3; i++)
        pthread_join(id[i], NULL);
    pthread_mutex_destroy(&io_mutex);
}


其運(yùn)行結(jié)果為:
  thread 0: 1
  thread 0: 2
  thread 1: 1
  thread 2: 1
  thread 1: 2
  thread 2: 2
  thread 1: 3
  thread 2: 3
  thread 0: 3

由此我們可以看出,盡管看起來(lái)在各個(gè)線程中訪問(wèn)的都是同樣的*value,但實(shí)際上訪問(wèn)的卻是各自的線程專有的對(duì)象。而且除了初始化以外,對(duì)這些對(duì)象的訪問(wèn)無(wú)需進(jìn)行序列化。因?yàn)長(zhǎng)oki::SmartPtr 為我們做了大量的工作,TS_SPStorage 的實(shí)現(xiàn)十分簡(jiǎn)潔明了。有興趣的讀者,可以對(duì)這里的實(shí)現(xiàn)與Boost 的thread_specific_ptr 進(jìn)行比較。像這樣對(duì)Loki::SmartPtr 的策略進(jìn)行的定制,在理論上數(shù)目是無(wú)限的,也就是說(shuō),通過(guò)它我們可以擁有五花八門、“千奇百怪”的智能指針——了不起的Loki,不是嗎?有了這樣的模板類,我們就再不需要一次一次地為標(biāo)準(zhǔn)C++增加新的智能指針類型了。


三、 結(jié)束語(yǔ)
  在推進(jìn)器Boost 和斯堪的納維亞之神Loki 的戰(zhàn)爭(zhēng)中,誰(shuí)將勝出恐怕已不言而喻。倘若扮演上帝的C++標(biāo)準(zhǔn)委員會(huì)起了偏心,硬要選中Boost 的智能指針?lè)桨傅脑挘敲丛诮酉聛?lái)的日子里,他們就將不再是C++標(biāo)準(zhǔn)委員會(huì),而是C++智能指針救火委員會(huì)了。而即便那樣,Loki,斯堪的納維亞之神,也仍將矗立在C++大陸上,為他的“子民”——C++程序員們——帶來(lái)統(tǒng)一、秩序和和諧。

相關(guān)資源
1. Herb Sutter,The New C++: Smart(er) Pointers,見C++ User Journal 網(wǎng)站:http://www.cuj.com/experts/2008/sutter.htm
2. Boost 文檔:http://www.boost.org
3. Andrei Alexandrescu,Modern C++ Design 及Loki,見http://www.moderncppdesign.com或http://www.awprofessional.com/catalog/product.asp?product_id=%7B4ED3E6F3-371F-4ADC-9810-CC7B936164E3%7D。
4. Douglas C. Schmidt、Timothy H. Harrison 和Nat Pryce,Thread-Specific Storage for C/C++,見http://www.cs.wustl.edu/~schmidt/或http://www.flyingdonkey.com/ace/(中文)。
5. Herb Sutter,Template Typedef,見http://www.gotw.ca/gotw/079.htm。
6. PThreads-Win32 庫(kù),見http://sources.redhat.com/pthreads-win32/

posted on 2007-08-21 09:00 安帛偉 閱讀(457) 評(píng)論(0)  編輯 收藏 引用 所屬分類: BOOST
青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品
  • <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>
            亚洲专区在线视频| 女人天堂亚洲aⅴ在线观看| 每日更新成人在线视频| 欧美在线视频在线播放完整版免费观看| 一区二区三区四区精品| 99国产精品久久久| 亚洲一区二区三区久久| 性色av一区二区三区红粉影视| 午夜欧美精品久久久久久久| 久久久久国产精品一区二区| 久久久久青草大香线综合精品| 女仆av观看一区| 亚洲精品视频在线观看网站| 亚洲免费中文| 久久色在线观看| 欧美日韩国产小视频| 国产免费观看久久黄| 亚洲区中文字幕| 欧美一区二区成人| 欧美激情亚洲精品| 亚洲在线观看免费| 欧美激情偷拍| 伊人久久大香线| 在线综合欧美| 老司机免费视频久久| 一区二区三区免费网站| 久久久噜噜噜| 国产欧美日韩91| 亚洲伦理中文字幕| 欧美一区亚洲一区| 亚洲精品乱码| 久久久国产精品一区二区中文| 欧美精品国产一区二区| 国产一区二区三区成人欧美日韩在线观看| 亚洲第一综合天堂另类专| 亚洲欧美日韩精品一区二区| 欧美国产一区二区三区激情无套| 亚洲在线成人| 欧美日韩亚洲免费| 最新精品在线| 欧美不卡高清| 性xx色xx综合久久久xx| 欧美手机在线视频| 99伊人成综合| 欧美高清视频在线| 久久麻豆一区二区| 国模套图日韩精品一区二区| 亚洲欧美日韩网| 亚洲天堂第二页| 性欧美大战久久久久久久免费观看| 亚洲一区二区三区高清| 美日韩精品视频免费看| 夜夜嗨av一区二区三区网页 | 黄色工厂这里只有精品| 亚洲自拍偷拍网址| 日韩一级二级三级| 欧美日韩福利视频| 日韩一区二区久久| 亚洲精品国产精品乱码不99 | 99国产精品99久久久久久粉嫩| 久久欧美肥婆一二区| 亚洲欧美激情精品一区二区| 欧美午夜免费电影| 亚洲欧美不卡| 亚洲综合精品自拍| 国产自产在线视频一区| 久久这里只有| 欧美不卡三区| 在线亚洲一区| 亚洲欧美日韩中文在线制服| 国产一区观看| 欧美国产日韩一区二区三区| 欧美jizz19性欧美| 中文欧美日韩| 亚洲视频在线一区| 国产午夜精品视频| 蜜桃av一区二区在线观看| 免费看av成人| 中文久久乱码一区二区| 亚洲欧美激情精品一区二区| 韩国一区二区三区美女美女秀| 美女视频一区免费观看| 免费视频亚洲| 亚洲欧美另类久久久精品2019| 欧美一区=区| 亚洲人成人99网站| 亚洲欧美久久久久一区二区三区| 狠狠色香婷婷久久亚洲精品| 亚洲丶国产丶欧美一区二区三区| 欧美男人的天堂| 久久av一区二区三区亚洲| 六月婷婷一区| 亚洲免费网站| 久热这里只精品99re8久| 99国产精品久久久久久久成人热| 亚洲一区在线看| 亚洲国产欧美在线人成| 亚洲一区久久久| 亚洲高清资源综合久久精品| 在线视频中文亚洲| 在线播放中文字幕一区| 99视频在线精品国自产拍免费观看 | 国产精品永久免费| 亚洲愉拍自拍另类高清精品| 久久国产婷婷国产香蕉| 亚洲精一区二区三区| 午夜在线a亚洲v天堂网2018| 亚洲精品在线视频观看| 欧美一级艳片视频免费观看| 日韩一区二区精品在线观看| 欧美中在线观看| 亚洲一区二区伦理| 欧美成人高清| 玖玖玖国产精品| 国产日韩久久| 亚洲小说欧美另类婷婷| 一区二区电影免费观看| 另类春色校园亚洲| 久久手机免费观看| 国产精品视频1区| 亚洲午夜羞羞片| 亚洲永久免费观看| 欧美日韩在线看| 亚洲日本中文| 99av国产精品欲麻豆| 欧美国产免费| 亚洲国产一区二区三区青草影视| 在线观看日韩av电影| 久久麻豆一区二区| 免费永久网站黄欧美| 激情五月综合色婷婷一区二区| 午夜视频久久久久久| 欧美在线3区| 国产一区二区三区在线观看精品 | 日韩午夜一区| 女人色偷偷aa久久天堂| 欧美高清在线播放| 在线欧美日韩国产| 久久综合网络一区二区| 麻豆成人在线| 亚洲国产精品成人综合色在线婷婷 | 欧美日本中文字幕| 亚洲精品免费一二三区| 日韩亚洲欧美成人一区| 欧美片第1页综合| 夜夜爽av福利精品导航| 亚洲午夜精品福利| 国产精品免费视频xxxx| 午夜精品久久久久久久久| 久久国产精彩视频| 依依成人综合视频| 欧美激情二区三区| 一区二区三区成人精品| 欧美一区二区精品| 狠狠久久五月精品中文字幕| 久久在线免费观看| 亚洲精品乱码久久久久久蜜桃麻豆 | 国产精品久久久久久一区二区三区| 一区二区激情小说| 欧美一级艳片视频免费观看| 国产麻豆日韩| 国产一区三区三区| 亚洲欧美日韩精品一区二区| 久久九九国产精品| 最近看过的日韩成人| 欧美色播在线播放| 欧美影片第一页| 欧美激情亚洲激情| 亚洲欧美日韩一区二区三区在线观看| 国产精品一区二区在线观看网站| 久久精品国内一区二区三区| 91久久在线视频| 欧美一级一区| 亚洲精品久久久久久久久久久久久 | 亚洲午夜女主播在线直播| 久久男人av资源网站| 9久re热视频在线精品| 国产美女扒开尿口久久久| 欧美成人亚洲| 久久成人一区二区| 一本久久青青| 欧美二区在线| 久久成人免费电影| 一卡二卡3卡四卡高清精品视频| 国产日韩欧美中文| 欧美日韩精品免费观看视频| 久久精品欧美| 国产精品99久久久久久久久| 美女视频黄 久久| 欧美与欧洲交xxxx免费观看| 亚洲精品久久久久久久久久久久| 国产乱码精品| 欧美午夜精品久久久| 免费一级欧美片在线播放| 香蕉成人伊视频在线观看| 日韩一级片网址| 亚洲精品国产精品国产自| 麻豆av一区二区三区久久| 香蕉久久一区二区不卡无毒影院| 日韩一区二区久久|