2011-03-11 50 views
9

今天我學習了C++的「成員空間」成語,它粗略地濫用了C++的一個屬性,它使T::bar以及T.bar工作,當T既是一個範圍內的類型也是對象。使用「會員空間」成語嗎?

struct A { 
    struct Controls { 
    /* put some typedefs/data/functions here */ 
    } Controls; 
}; 

// Can be used as a type and value 
A a; 
A::Controls::iterator it = a.Controls.begin(); 

你有沒有在實踐中使用過這個習語?你覺得它有用嗎?這個習語有什麼好處或最好的應用?

+3

Eeewww!令人討厭!布萊什! – 2011-03-11 00:29:07

回答

6

沒有,我從來沒有使用過這種技術(我不認爲它值得被稱爲「成語」):

因爲我沒有用它,我還沒有發現它有用。

該技術的一個很好的應用可能會混淆其他程序員。

另一個應用程序可能是編寫一個技術喋喋不休的文章,講述它對於一些想象中的從未遇到過的問題有多棒,也許是用大量模板元編程混淆了?我記得,最好的應用可能是寫一篇關於所有這些愚蠢規則的文章,就像你還可以在同一範圍內有一個相同名字的函數struct一樣,那些能夠完成的事情,可以通過遠離語言的黑暗角落來完成得更好。 :-)文章在金錢上付出的代價不大,但是他們付錢並且寫作很有趣。請寫下來(TIA)。

乾杯&心連心,

+2

關於此的文章在http://accu.org/index.php/journals/1527。我的同事告訴我這個成語/「成語」。 – 2011-03-11 00:35:24

+1

@litb:「這是合法的,儘管它沒有明確的目的,但我發現它是一種識別代碼中成員空間的簡單方法。」 - nuff說。那些寧願用簡單的方法區分類名和對象名的方法也不需要使用它:-)我不認爲這個命名約定對文章中描述的習語至關重要。這個成語在其他方面看起來相當明智,在簡單的例子中它只是暴露了公共接口中對象的組成。 – 2011-03-11 00:42:29

+0

呵呵,關於保持對包含對象的引用的入侵式和非入侵式技術的內容是一種在Java中重現內部類的功能的曲折方式。對於'offsetof'的狡猾使用,我傾向於Alf所說 - 可能更多地用作文章 - 飼料而不是實踐,如果你不想讓你的同事狩獵你並殺死你...... – 2011-03-11 00:49:04

1

不,我從來沒有使用的。

它有很好的用處嗎?也許你可以用它來向你的同事展示你是他們更好的......就像一些pleople在他們不知道的地方使用模板一樣,爲一個簡單問題提供了一個複雜的解決方案(請注意,與會員空間慣用語不同,模板有時是有用的)。

+3

有時有用嗎?如何非常有用,有時不合適? – GManNickG 2011-03-11 03:02:58

+1

是的,它通常是非常有用的,但它是缺乏經驗的程序員最經常使用的C++特性之一。 – fbafelipe 2011-03-11 03:37:49

+0

請注意詳細說明它有用嗎? – 2011-03-11 06:14:23

2

由於@Steve在評論中已經說過,嵌套類型和這種類型的實例具有相同的名稱這一事實並不是該技術的核心部分。爲了增加封裝,我們甚至可以使用成員函數來訪問實例(儘管它看起來不像名稱空間限定)。例如,你給的例子可以改寫如下沒有任何缺點(當然,也許有,但我無法找到任何此刻):

struct A 
{ 
    struct Controls 
    { 
     //typedefs/data/functions 
    }; 

    const Controls & getControls() { return controls_; } 

    private: 

    Controls controls_; 
}; 

A a; 
A::Controls::iterator = a.getControls().begin(); 

這裏是我看到memberspaces。成員空間的目標是劃分一個類的命名空間,以便將相關的typedef和方法組合在一起。上面的Controls類可以在A之外定義,但它與A緊密連接(每個AControls關聯,反之亦然,Controls只不過是包含它的對象A的視圖)認爲使其成爲A的成員感覺「自然」,並且可能還使其與A(如果需要訪問A的內部結構)成爲朋友。

所以基本上,成員空間讓我們在單個對象上定義一個或多個視圖,而不會污染封閉的類名稱空間。正如文章中指出的那樣,當你想提供幾種迭代類的對象的方法時,這可能非常有趣。

例如,我們假設我正在編寫一個代表C++類的類;我們稱之爲Class。一個類有一個名字,和它所有基類的列表。爲了方便起見,我想讓一個Class也存儲所有從它繼承的類(它的派生類)的列表。所以我有一個這樣的代碼:

class Class 
{ 
    string name_; 
    list< shared_ptr<Class> > baseClasses_; 
    list< shared_ptr<Class> > derivedClasses_; 
}; 

現在,我需要一些成員函數添加/刪除基類/派生類:

class Class 
{ 
    public: 

    void addBaseClass(shared_ptr<Class> base); 
    void removeBaseClass(shared_ptr<Class> base); 

    void addDerivedClass(shared_ptr<Class> derived); 
    void removeDerivedClass(shared_ptr<Class> derived); 

    private: 

    //... same as before 
}; 

有時,我可能需要添加一個方法在基類和派生類迭代:

class Class 
{ 
    public: 

    typedef list< shared_ptr<Class> >::const_iterator const_iterator; 

    const_iterator baseClassesBegin() const; 
    const_iterator baseClassesEnd() const; 

    const_iterator derivedClassesBegin() const; 
    const_iterator derivedClassesEnd() const; 

    //...same as before 
}; 

我們面對的仍然是可控的名字的數量,但是,如果我們要添加反向迭代是什麼?如果我們更改存儲派生類的基礎類型,該怎麼辦?這將增加另一堆typedefs。此外,您可能已經注意到,我們提供對開始和結束迭代器的訪問方式不符合標準命名,這意味着我們無法使用通用算法(如Boost.Range),而無需額外的工作。

事實上,在查看成員函數名稱時我們很明顯地看到,我們使用前綴/後綴對它們進行邏輯分組,這是我們現在嘗試避免的,因爲我們有名稱空間。但是因爲我們不能在類中使用名稱空間,所以我們需要使用一個技巧。

現在,使用成員空間,我們將所有基本相關和派生相關的信息封裝在它們自己的類中,這不僅讓我們將相關數據/操作組合在一起,還可以減少代碼重複:由於操作基類和派生類是一樣的,我們甚至可以使用一個單一的嵌套類:

​​

現在我可以這樣做:

Class c; 
Class::DerivedClasses::const_iterator = c.derivedClasses.begin(); 

boost::algorithm::find(c.derivedClasses, & c); 
... 

在此示例中,嵌套類不那麼耦合到Class,所以可以定義爲o utside,但你可以找到一個更強大的範例。


好吧,在這篇長文後,我注意到我沒有真正回答你的問題:)。所以不,我從未在我的代碼中實際使用過成員空間,但我認爲它有它的應用程序。

我已經考慮了一次或兩次,特別是當我爲一個圖書館編寫一個外觀類時:外觀旨在通過擁有一個入口點使圖書館更易於使用,但是因此它有幾個成員功能都是相關的,但具有不同程度的「相關性」。而且,它表示一組對象,所以它除了包含「面向特徵」的成員函數之外,還包含與迭代有關的typedef和成員函數。我考慮使用成員空間將邏輯「子空間」劃分爲一個更清晰的接口。不知道爲什麼我沒有做到這一點。

+0

免責聲明:在這個答案寫的代碼是「即興」,所以它可能包含大量的錯誤,錯誤和可以改進的部分。 – 2011-03-11 17:02:14

1

無論是否潛在混淆與否,總是歡迎任何語法靈活性。其實祕訣就在於boost.array使用,這是一個最小的實現:

#include<cassert> 

template<unsigned N> 
struct fixed_array{ /* bla bla */ 
    static unsigned size(){ 
     return N; 
    } 
}; 


int main(){ 
    fixed_array<3> arr; 
    assert(arr.size() == 3);    //like a stl container 
    assert(fixed_array<3>::size() == 3); //more proper, but less generic wrt stl containers 
    return 0; 
} 

因此如果用戶希望看到一個靜態成員函數/靜態成員變量/嵌套類的實例,而不是一個屬性那麼他/她可以。例如,用於編寫通用代碼很有用,在這個例子中它根本不會造成混淆。

相關問題