對於"Why should I have written ZeroMQ in C, not C++",我想說...

ZeroMQ的做者在文章"Why should I have written ZeroMQ in C, not C++ (part I)""Why should I have written ZeroMQ in C, not C++ (part II)"中列舉了他用C++實現ZeroMq時遇到的一些問題,藉此批評了C++和麪向對象設計。在Part I中他批評了C++的exception機制。對此我以爲沒什麼好說的,C++本就是一個多範式語言,C++之父說過在C++中你沒必要爲你不使用的特性付出任何代價。若是以爲C++的異常機制在你的開發場景下不能知足要求,想要像C那樣靠返回值進行錯誤處理,那麼你只用關閉exception特性(在gcc下增長-fno-exception參數),而後像C那樣去處理錯誤便可。這並非C++的錯,只是你使用它的方式不對而已!咱們在嵌入式下一直使用C++,咱們的基礎設施中有一套斷言宏可讓這種靠返回值的錯誤處理代碼更加簡潔和優雅,具體參考ccinfra的base組件中的斷言機制。git

在Part II中做者批評了C++ STL庫中的list致使了更多的內存分配和內存碎片以及由此引起的性能問題。以下原文中的代碼和圖示:程序員

// C++程序的鏈表
class person
{
    int age;
    int weight;
};

std::list <person*> people;
// C程序的鏈表
struct person
{
    struct person *prev;
    struct person *next;
    int age;
    int weight;
};

struct
{
    struct person *first;
    struct person *last;
} people;

兩種鏈表在內存結構上的差別:github

做者把該問題最後推演到是C++和麪向對象設計的問題。我想說的是,C++的設計哲學是給你提供強大抽象手段的同時,讓你仍然能夠在任何特性和性能之間去取捨和平衡。上述問題並非C++或者面向對象的問題,其實當咱們真正掌握了C++的設計哲學,咱們徹底能夠設計出一種既能像面向對象那般地使用,又能夠兼顧像C那樣內存佈局的list,這就是ccinfra提供的List組件。事實上咱們已經在嵌入式開發中很愉快地使用該組件不少年了!佈局

struct Foo : ListElem<Foo>
{
    Foo(int a) : x(a)
    {
    }

    int getValue() const
    {
        return x;
    }

private:
    int x;
};

TEST(...)
{
    List<Foo> elems;

    ASSERT_TRUE(elems.isEmpty());
    ASSERT_EQ(0, elems.size());

    Foo elem1(1), elem2(2), elem3(3);

    elems.pushBack(elem1);
    elems.pushBack(elem2);
    elems.pushBack(elem3);

    ASSERT_EQ(&elem1, elems.getFirst());
    ASSERT_EQ(&elem3, elems.getLast());
    ASSERT_EQ(3, elems.size());

    Foo* first = elems.popFront();
    ASSERT_EQ(1, first->getValue());
    ASSERT_EQ(2, elems.size());

    int i = 2;
    LIST_FOREACH(Foo, elem, elems)
    {
        ASSERT_EQ(i++, elem->getValue());
    }
}

ccinfra中的List是一個雙向鏈表,某一類型T只有繼承了ListElem ,才能夠插入List 中去。繼承了ListElem 的節點內存佈局和C習慣中的鏈表節點內存佈局是同樣的,鏈表指針和節點數據是綁定在一塊兒的,而List對象中只包含一個頭節點以及統計當前鏈表大小的變量。List接收節點並完成節點先後鏈表指針的連接,可是並不拷貝節點,因此對於節點的內存管理徹底由程序員決定,List並不作任何假定。 性能

從上面的例子中能夠看到ccinfra的List組件用法和std::list相似,封裝了各類接口以及提供了正逆向迭代器,同時還提供了一些方便遍歷的輔助宏。關於ccinfra的List的更多用法,具體參考該組件的實現(ccinfra/include/ctnr/list)和測試源碼(ccinfra/test/TestList.cpp)。測試

經過上面的例子也能夠看出,若是鏈表指針和節點數據內存在一塊兒,那麼哪些類型能夠做爲鏈表元素是提早肯定好的,而STL庫中的std::list卻能夠指向任何元素,並不須要提早規劃。固然有利就有弊,後者確實須要分配更多的內存塊。可是這並非C++或者面向對象的問題,只是STL做爲一個基礎庫,它選擇了通用性。若是你須要偏向性能,那麼就能夠按照本身的使用方式來實現一個list,可是你必須知道你爲此放棄了什麼。這正是C++的強大之處,它給了你選擇的自由,讓你能夠盡最大的努力去取得更好的平衡。正如ccinfra的List,它兼容了C的內存結構,保證了性能,但誰又能說它的用法不OO呢?設計

ccinfra的github地址:https://github.com/MagicBowen/ccinfra
做者:MagicBowen,Email:e.bowen.wang@icloud.com,轉載請註明做者信息,謝謝!指針

相關文章
相關標籤/搜索