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

woaidongmao

文章均收錄自他人博客,但不喜標題前加-[轉貼],因其丑陋,見諒!~
隨筆 - 1469, 文章 - 0, 評論 - 661, 引用 - 0
數據加載中……

Generic:Traits on Steroids

      以前我們介紹了traits的一些基本用法,大家感覺如何?這次我們接著介紹traits的一些更為有趣的用途,讓大家興奮一下(Steroid是類固醇,常見的興奮劑)。
   
在上期的Generic<Programming>[1]中,我們討論了traits模板和traits類。這篇文章進一步討論traits對象和全層次(hierarchy-widetraits
    Traits
技術很有用,但是什么時候你需要這種非凡的靈活性呢?如果你用了traits,你怎么才能避免手工向現有類層次中的大量的類添加traits 的苦差事呢?這篇文章以上一次的SmartPtr為例,解答這些問題。特別是介紹了全層次(hierarchy-widetraits,一項非常 coolC++新技術,可以讓你一下子為整個類層次而不僅是單個類定義traits
   
回到SMARTPTR
   
上一次的專欄里介紹了一個smart pointer,它可以根據客戶對模板實例化的方式不同而用于單線程或多線程的代碼中。再來看一下SmartPtr的定義:

template <class T, class RCTraits = RefCountingTraits<T> >
class SmartPtr
{
...
};

RefCountingTraits可以對SmartPtr進行定制,以適應不同類型T所使用的引用計數的語法和語義。如果你要在單線程代碼中用 SmartPtrRefCountingTraits就可以了。否則,你必須另外提供一個traits類(MtRefCountingTraits)作 為第二個模板參數。MtRefCountingTraits保證在多線程情況下,引用計數是安全的。
class MtRefCountingTraits
{
static void Refer(Widget* p)
{
// serialize access
Sentry s(lock_);
p->AddReference();
}
static void Unrefer(Widget* p)
{
// serialize access
Sentry s(lock_);
if (p->RemoveReference() == 0)
delete p;
}
private:
static Lock lock_;
};

對于單線程的widget,客戶代碼可以用SmartPtr<Widget>,對于多線程的widget,可以用SmartPtr< Widget, MtRefCountingTraits>。如果沒有上一篇文章最后留下的那個問題,事情就這樣簡單。那個問題是:在多線程版的SmartPtr 里,哪一部分還是低效的?
正如很多讀者指出的那樣,問題在于MtRefCountingTraits用了類級別的加鎖操作。Herb Sutter形象地說:Static lock, bad juju!juju,符咒】當你進行串行化的操作時,比如MtRefCountingTraits::Refer(),類級別的鎖會把 MtRefCountingTraits類的所有對象都鎖住,因為lock_是所有MtRefCountingTraits實例共享的靜態變量。

如果你有很多線程頻繁的操作widgetsmart pointer,這可能會成為程序低效的一個根源。原本可能完全無關的線程在復制SmartPtr<Widget, MtRefCountingTraits>對象時,也必須排隊等待。

在對象級別上加鎖是解決這個問題的一個方法。要使用對象級別上的鎖,只需把MtRefCountingTraits的成員lock_改成非靜態的普通成員 變量,就可以對每一個對象單獨加鎖。但是這個方法的缺點是每個對象都因為增加了一個lock_變量而變大了。讓我們來實現對象級別加鎖的smart pointer

TRAITS
對象
當我們把對象級別加鎖的方法運用到SmartPtr上時,我們遇到了一個問題。讓我們再來看一下SmartPtr的析構函數的定義:

template <class T, class RCTraits = RefCountingTraits<T> >
class SmartPtr
{
private:
T* pointee_;
public:
...
~SmartPtr()
{
RCTraits::Unrefer(pointee_);
}
};

正如你所看到的那樣,根本沒有RCTraits的對象。SmartPtr的析構函數用靜態函數的語法調用RCTraits::Unrefer() SmartPtrRCTraits作為只有兩個靜態函數的包裝來使用。現在traits類需要保存一些狀態,所以我們開始討論如何保存traits對 象。顯然,保存traits對象的地方就在SmartPtr對象里,因此我們可以這樣修改代碼:
template <class T, class RCTraits = RefCountingTraits<T> >
class SmartPtr
{
private:
T* pointee_;
RCTraits rcTraits_;
public:
...
~SmartPtr()
{
rcTraits_.Unrefer(pointee_);
}
};

現在,SmartPtr擁有了一個Lock對象,并使用這個對象完成對象級別的加鎖操作,這正是我們想要的。屬于不同線程的SmartPtr對象不再共享任何數據,因此不會有任何同步的問題。問題解決了。
然而,SmartPtr變得更大了。這是顯然的,我聽到你說,我們首先要保證的就是多線程的SmartPtr擁有一個Lock對象。但是,不僅僅是多線程SmartPtr變大了,單線程的SmartPtr也變大了,盡管沒有任何附加的數據(回憶一下,RefCountingTraits沒有任何數 據成員)。這是為什么呢?因為C++中空對象的大小也不是0。這條規則在C++語言的很多地方都是合理的。(比如,如果有大小為0的對象的話,你怎樣才能 建立這樣的對象的數組?)

不管這條規則是否明智,至少在現在這種情況下,它對我們是不利的。SmartPtr<Something, RefCountingTraits<Something> >要比一個單純的指向T的指針大,這是不應該的。現在單線程的SmartPtr的大小至少是sizeof(T*)+1,但通常由于字對齊和字節填充的原因,最終SmartPtr對象大小可能會在2*sizeof(T*)左右。如果你有很多單線程的SmartPtr,尺寸增加的代價會變得很顯著,更不 用說通過傳值方式傳遞SmartPtr的附加消耗了。

幸運的是,C++標準中有另一條關于對象大小的規則,可以幫助我們解決這個問題。這就是空基類優化(empty base optimization)。如果類D的基類B是空的(沒有非靜態數據成員),那么D對象中的B子對象的有效大小可以是0。這并沒有違反前面那條規則,因 為B子對象被包含于D對象中;當然,如果你抽出一個單獨的B對象時,它還是有非0的大小。你是否可以使用空基類優化取決于你的編譯器,因為這條規則的實現是可選,而不是必需的。MetrowerksCode Warrior 5.xMicrosoft Visual C++ 6.0都實現了空基類優化。還有其他地方也需要使用這個優化。(注:這幾個編譯器里所包含的Standard C++ Library中,在containers的實現時利用了空基類優化。每一個標準的container都聚合了一個allocator對象,缺省的 allocator通常是一個空類【編者:請參考《C++空成員優化》一文】。)

把空基類優化應用到前面的SmartPtr代碼中,我們可以讓SmartPtr繼承RCTraits,而不是用聚合。通過這種方式,如果RCTraits是空的,編譯器會通過優化去掉多余的空間;如果RCTraits不是空的,那么結果和聚合的情況一樣。

我們應該用那種繼承呢?privateprotected,還是public?不要忘了這只是一種實現上的優化,而不是概念的變化。不管怎么說,SmartPtr不是一個RCTraits。因此,最好的選擇是私有繼承。

template <class T, class RCTraits = RefCountingTraits<T> >
class SmartPtr : private RCTraits
{
private:
T* pointee_;
public:
...
~SmartPtr()
{
RCTraits::Unrefer(pointee_);
}
};

這只是利用繼承來優化對象大小的一個技巧。有趣的是,我們又回到了用兩個冒號的寫法,因為現在RCTraitsSmartPtr的基類。
traits需要保持狀態時,就需要用traits對象了。Traits對象可以是其他對象的一部分,也可以作為參數傳遞。當traits對象可能為空時,也許以可以考慮用繼承的技巧來優化對象的內存布局,當然你的編譯器要支持空基類優化。

定義:traits對象是traits類的一個實例。
Definition: A traits object is an instance of a traits class.

插曲
Traits
模板, traits, traits對象……當我們的討論從純粹的靜態代碼生成方式轉變到具有狀態的實體時,我們的表達方式也從最靜態的方式(模板)發展到具有更多動態特性的方式(完整的traits對象)。Traits模板完全是一種編譯時的機制;它們在編譯結束前就已經消失了。在另一個極端,traits對象是具有狀態和行 為的動態實體。

更進一步的動態化是使用多態traitstraits對象的指針或者引用。但那已經超出traits的范疇了。確切地說,多態traits是一個策略(Strategy)設計模式。[2]

使用能滿足要求的任何一種traits機制,并且盡可能選擇靜態的方案。相對于運行時的解決方案,我們一般更傾向于選擇編譯時的解決方案。編譯時的解決方案意味著:編譯器會對代碼進行更好的檢查,并且往往生成的代碼有更好的效率。當然,另一方面,動態【注:dynamism,這里一語雙關,也可解釋為活力,有生氣】為生活帶來情趣。

全層次TRAITS
Traits
往往不是只用于單個類型,而是用于整個類層次。例如,引用計數的方法通常對于整個類層次都是一樣的。如果不用對于每個類都手工添加 traits,而能夠定義一個traits可以用于整個類層次,那就好了。但是,traits技術的基礎模板對于繼承是一無所知的。這怎么辦呢?

也許一個好設計的首要準則是要有靈活性,不要局限于一種策略。解決設計問題就像攻打堅固的城堡:如果一個策略不行,最好就換另外一個。一個壞的策略可能也能解決問題,但是比其他方法代價更高。

根據這個想法,我們重新理一下思路。我們需要找一種在類型層次中保持不變的東西,用它來建立一個類模板。你猜那是什么?嵌套類(在類中定義的類)!除非你重新定義,嵌套類在繼承過程中是不變的。嵌套類可以像其他符號一樣被繼承。這看上去是個值得一試的方法。為了能自動加上嵌入的類型定義,我們先做一個簡單 的模板:

template <class T>
struct HierarchyRoot
{
// HierarchyId is a nested class
struct HierarchyId {};
};

比如說我們有一個以Shape為根的類層次(圖1)。為了表示Shape是根,你可以讓它繼承Hierarchy<Shape>,如下所示。其他類不變。



1 Shape層次

class Shape : public HierarchyRoot<Shape>
{
...
};
class Rectangle : public Shape
{
...
};

如果你想防止從ShapeHierarchyRoot<Shape>的隱式類型轉換(通常也是不希望的),你可以這樣定義Shape
class Shape : private HierarchyRoot<Shape>
{
...
public:
using HierarchyRoot<Shape>::HierarchyId;
};

我們得到一個關鍵的結果:Rectangle::HierarchyIdShape::HierarchyId是同樣的類型。不論你直接或者間接地從Shape派生新類,只要你不重新定義符號HierarchyId,這個符號代表的類型就在整個繼承體系中保持不變。
要設計一個使用全層次traitsSmartPtr和設計使用普通traitsSmartPtr一樣簡單。你只要用T::HierarchyId代替T就行了,象這樣:

template <class T, class RCTraits = RefCountingTraits<typename T::HierarchyId> >
class SmartPtr
{
...
};

現在,假設在你的應用程序中有兩個類層次關系:一個以Shape為根,另一個以Widget為根。象Shape一樣,WidgetHierarchyRoot<Widget>繼承。現在你可以這樣為兩個類層次特化RefCountingTraits
template <>
class RefCountingTraits<Shape::HierarchyId>
{
...
};
template <>
class RefCountingTraits<Widget::HierarchyId>
{
...
};

就是這樣,上面的traits可以正確的應用于在兩個類繼承體系中的類,甚至對還沒有定義的類也沒有問題。下面兩節中將指出,全層次traits是相當靈活的。
定制全層次TRAITS
簡單的traits可以為每個類型提供特化;全層次traits為每個類層次提供特化。有時候你可能遇到介于兩者之間的情況:你為整個繼承體系提供了traits模板,同時也要對體系中單獨的一個或兩個類型進行特化。

你可以這樣定義traits模板來達到目的:

template <class HierarchyId>
class HierarchyTraits
{
... most general traits here ...
};
template <class T>
class Traits
: public HierarchyTraits<T::HierarchyId>
{
// empty body - inherits all symbols from base class
};

這個traits模板怎樣工作呢?客戶代碼可以這樣使用:Traits<Shape>Traits<Circle>等。若想特 化整個Shape層次的traits,我們可特化HierarchyTraits<Shape::HierarchyId>。缺省情況下,因為Traits<T>繼承HierarchyTraits<T::HierarchyId>,所有Shape的派生類會使用 HierarchyTraits<T::HierarchyId>里定義的traits。(我敢打賭,如果你跟蹤所有這些符號的來源,你會得到很多樂趣。其實這很簡單,HierarchyTraits對應于整個層次,Traits對應于每個類型。)
如果你想特化一個特定的類的traits,比如說Ellipse,你可以直接特化Traits模版:

template <>
class Traits<Ellipse>
{
... specialized stuff for Ellipse ...
};

你可以選擇是否繼承HierarchyTraits<Ellipse>。如果你只想重寫一兩個符號,你可以選擇繼承;如果你想完全重寫Ellipsetraits,你可以選擇不繼承。這完全取決于你。
對于剛才提到的繼承的使用還有一點說明:從動態多形的觀點來看,事實上Traits<T>繼承HierarchyTraits<T:: HierarchyId>是不恰當的,因為HierarchyTraits不是一個具有多態性的基類。我們在這里用繼承是因為另一個理由:把繼承作 為符號傳遞的工具,目的是讓Traits<T>具有HierarchyTraits<T::HierarchyId>里定義的所有符號。繼承不僅可以用來實現動態的多態性,也可以用來在編譯時操作類型。

用這一節里介紹的Traits-HierarchyTraits方法,可以對類層次traits根據每個類型進行特化。在上面討論的例子中, Traits<Rectangle>Traits<Circle>用的是共同的HierarchyTraits< Shape::HierarchyId>,而Traits<Ellipse>用的是特化了的Traits< Ellipse>。實現這一切不需要做太多的手腳。

子層次的TRAITS
假設你要對于Shape的類層次的一個子層次重新定義traits,比如圖中以Bitmap為根的子樹(圖2)。因此你需要特化Bitmap和它的直接或間接子類的traits

為了達到這個目的,你可以讓Bitmap同時繼承ShapeHierarchyRoot<Bitmap>。然后你必須通過using語句來消除符號HierarchyId的二義性,像這樣:



2 Shape層次的Bitmap子層次

class Bitmap : public Shape,
public HierarchyRoot<Bitmap>
{
...
public:
using HierarchyRoot<Bitmap>::HierarchyId;
};

通過using語句,Bitmap類會優先使用HierarchyRoot<Bitmap>::HierarchyId,而不是 Shape::HierarchyId。這樣你可以用Bitmap::HierarchyId來特化traits,并且Bitmap的子類也將用這個特 化,除非你為某個子層次又定義了不同的traits
注意事項
全層次traits的最大的缺點是需要修改類層次中的基類,而你有時候不能做到這點。還好,你會得到一個編譯錯誤("Class Widget does not define a type HierarchyId"),而不是運行時的錯誤。

你可以對于無意義類型(如void)特化全層次traits模板,這樣可以在一定程度上解決這個問題。對于你不能修改的類層次,你可以用HierarchyTraits<void>。雖然不是很靈活,但在開發受阻時,也不失為一個可行的方法。

還有其它不用介入類層次來實現全層次traits的方法,但那些方法往往更脆弱,并且暴露出各種錯誤。我始終歡迎讀者提出各種建議。

結論
trait必須保持某些狀態的時候,就需要用到traits對象。如果trait類的狀態是可選的(有些traits有狀態,有些沒有),那么最好是通過繼承的技巧,利用空基類優化(如果可能的話)。

只需要一點點手腳,你就可以定義全層次traits。通過這種方式,你只需為一個類層次寫一次traits。全層次traits可以提供很大的靈活性,你可以對類層次中一個特定的類定義特別的traits,也可以對一個子層次定義traits

全層次traits使用繼承的方法比較怪異。繼承不僅僅是實現運行時多態性的工具,也是編譯時操縱類型的工具。C++把繼承的兩種特性混合在一起,有時候會引起誤解。

然而,最好的消息是,全層次traits只用了模板的基本功能。就是說,即使你現在使用的編譯器不那么符合標準,你還是可以用這個技術。

感謝
非常感謝Herb Sutter, 他花時間審閱了這篇文章并提出深刻的見解。

posted on 2008-11-09 01:20 肥仔 閱讀(588) 評論(0)  編輯 收藏 引用 所屬分類: C++ 模板

青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品
  • <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>
            欧美激情1区| 精品成人一区二区三区四区| 欧美理论在线播放| 亚洲一区二区三区精品在线| 亚洲欧美在线x视频| 在线日韩成人| 亚洲你懂的在线视频| 国产在线精品成人一区二区三区| 午夜精品一区二区三区在线播放 | 国产视频久久| 久久蜜臀精品av| 欧美三日本三级三级在线播放| 久久国产夜色精品鲁鲁99| 欧美日韩国产一区二区三区| 久久综合九色九九| 亚洲国产精品999| 国产精品久久九九| 亚洲国产第一页| 国产欧美va欧美va香蕉在| 久久影视精品| 国产精品免费看久久久香蕉| 亚洲人成网站色ww在线| 夜夜嗨一区二区| 欧美精品1区2区3区| 欧美高清你懂得| 亚洲黄网站黄| 欧美日韩八区| 香蕉成人啪国产精品视频综合网| 久久精品一级爱片| 国产欧美日本一区视频| 久久精品动漫| 亚洲卡通欧美制服中文| 亚洲综合激情| 欧美成人精精品一区二区频| 亚洲精品国产精品乱码不99| 亚洲欧美久久久| 国产精品久久999| 久久久av毛片精品| 9久re热视频在线精品| 欧美一区免费视频| 原创国产精品91| 欧美激情视频网站| 欧美一区二区视频免费观看| 老牛影视一区二区三区| 亚洲一本大道在线| 亚洲电影激情视频网站| 国产欧美精品| 久久精品视频网| 欧美bbbxxxxx| 亚洲免费人成在线视频观看| 91久久国产综合久久| 国产精品毛片大码女人| 欧美精品免费视频| 夜夜爽www精品| 国产精品久久久久av| 久久成人国产| 亚洲一区二区三区高清不卡| 亚洲激情成人| 亚洲性图久久| 亚洲欧洲精品一区二区三区不卡 | 欧美日韩亚洲视频一区| 午夜精品福利在线| 日韩一区二区久久| 亚洲欧洲一区二区三区久久| 亚洲欧美日韩人成在线播放| 一区二区三区三区在线| 亚洲在线视频一区| 国产欧美一区视频| 国产精品免费观看在线| 国产精品专区第二| 欧美日韩高清在线观看| 欧美大香线蕉线伊人久久国产精品| 性xx色xx综合久久久xx| 中文久久精品| 亚洲视频综合| 久久中文在线| 美女日韩在线中文字幕| 亚洲欧洲中文日韩久久av乱码| 亚洲国产欧美另类丝袜| 亚洲精品一区二区三区四区高清| 999在线观看精品免费不卡网站| 亚洲精品男同| 欧美亚洲尤物久久| 国产精品大全| 亚洲色图在线视频| 亚洲精选在线| 欧美精品亚洲二区| 中国成人黄色视屏| 亚洲免费av片| 欧美日韩在线播| 亚洲午夜电影在线观看| 欧美第一黄色网| 日韩视频免费在线观看| 91久久精品网| 欧美a级片一区| 亚洲欧洲日韩女同| 亚洲日本精品国产第一区| 国产精品久久久久久影视| 亚洲人人精品| 日韩一级黄色片| 国产精品国产馆在线真实露脸| 午夜精品999| 中文av一区特黄| 国产一区亚洲一区| 亚洲电影免费| 国产乱码精品| 欧美77777| 欧美日韩在线影院| 久久国产精品一区二区| 欧美xart系列在线观看| 在线视频一区观看| 午夜精品福利视频| 亚洲最新合集| 午夜精品久久久久久久久久久久| 国产亚洲精品福利| 亚洲国产小视频| 国产精品久久久久影院色老大| 老司机午夜精品视频| 欧美日韩精品在线播放| 欧美99在线视频观看| 欧美天天影院| 亚洲福利av| 最新日韩在线| 牛牛影视久久网| 久久精品国产第一区二区三区最新章节| 欧美好骚综合网| 久久一综合视频| 国外成人在线视频| 午夜视频在线观看一区二区三区| 一片黄亚洲嫩模| 麻豆成人在线播放| 欧美国产第一页| 亚洲精品婷婷| 欧美成人免费一级人片100| 久久天天躁狠狠躁夜夜av| 国产精品v欧美精品∨日韩| 亚洲国产成人在线播放| 亚洲激情第一页| 久久全国免费视频| 亚洲国产精品一区二区www| 亚洲电影网站| 欧美激情视频在线播放 | 亚洲国产日韩欧美在线99| 国产欧美一区二区视频| 亚洲午夜精品| 亚洲影院在线观看| 欧美午夜一区| 亚洲一区观看| 久久精品视频免费播放| 国产精品丝袜久久久久久app| 一本一本久久a久久精品牛牛影视| 99riav久久精品riav| 欧美精品免费播放| 亚洲精品一区中文| 中文亚洲视频在线| 国产欧美一区二区三区在线老狼 | 欧美日韩国产二区| 亚洲美女精品成人在线视频| 亚洲免费综合| 国产亚洲福利| 欧美chengren| 亚洲视频高清| 欧美岛国激情| 亚洲午夜免费视频| 国产亚洲午夜| 男女av一区三区二区色多| 亚洲国产精品久久久久婷婷884| 一二三区精品福利视频| 国产欧美日韩视频一区二区| 猛男gaygay欧美视频| 一区二区三区成人精品| 久久精品国产精品| 一个色综合导航| 经典三级久久| 欧美无乱码久久久免费午夜一区| 亚洲欧美一区二区激情| 欧美国产精品va在线观看| 亚洲一二三区在线| 在线免费观看视频一区| 国产精品高精视频免费| 久久久久久久999精品视频| 日韩一级精品| 欧美黄色成人网| 久久精品水蜜桃av综合天堂| 日韩天堂av| 伊人狠狠色j香婷婷综合| 欧美三级电影一区| 久久最新视频| 性视频1819p久久| 日韩视频免费观看| 欧美高清视频一区二区| 久久国产欧美精品| 亚洲一区二区三区精品在线| 亚洲精品国产无天堂网2021| 国产有码在线一区二区视频| 欧美日韩美女一区二区| 欧美1区3d| 另类av一区二区| 久久久成人精品| 欧美一区二区三区日韩视频|