对于"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,转载请注明做者信息,谢谢!指针

相关文章
相关标签/搜索