【问题标题】:Why does this list implementation take more space than stl list?为什么这个 list 实现比 stl list 占用更多空间?
【发布时间】:2016-11-13 04:41:45
【问题描述】:

我使用 IAR 作为嵌入式项目的编译器。我正在尝试为列表等基本类型引入一些模板,但是相对于我们当前的 C 样式实现,创建的每个 STL 列表对象都会增加大约 200 个字节的代码大小。 我尝试自己实现 STL 列表的一小部分,希望获得更小的代码占用空间,但最终比完整的 STL 列表更重。 我在使用模板时是否做错了什么?

谢谢

附:请注意,该代码未经测试,因此可能包含龙。

#ifndef __LINK_LIST_HPP__
#define __LINK_LIST_HPP__

#include <stdint.h>
#include <stdlib.h>

template <typename T> class list
{
private:
    struct LinkListElement
    {
        T payload;
        LinkListElement* next;
        LinkListElement* prev;
    };
public:

    class iterator
    {
        // Need access to LinkListElement struct
        friend class list;
    public:
        iterator() : m_cur_item(NULL){}

        iterator(LinkListElement* elem) : m_cur_item(elem){}

        iterator(const iterator& other) : m_cur_item(other.m_cur_item){}

        ~iterator(){}

        iterator& operator=(const iterator& other)
        {
            m_cur_item = other.m_cur_item;
            return *this;
        }

        bool operator==(const iterator& other) const
        {
            // Compare by position, ignoring the payload contents when comparing iterators.
            return  (m_cur_item->next == other.m_cur_item->next) &&
                    (m_cur_item->prev == other.m_cur_item->prev);
        }

        bool operator!=(const iterator& other) const
        {
            return !(*this == other);
        }

        // Prefix increment operator.
        iterator& operator++()
        {
            increment();
            return *this;
        }

        // Postfix increment operator.
        iterator operator++(int)
        {
            iterator copy(*this);
            increment();
            return copy;
        }

        // Prefix decrement operator.
        iterator& operator--()
        {
            decrement();
            return *this;
        }

        // Postfix decrement operator.
        iterator operator--(int)
        {
            iterator copy(*this);
            decrement();
            return copy;
        }

        T& operator*()
        {
            // Just so we won't crash, but behavior is undefined.
            if (m_cur_item == NULL)
            {
                return dummy;
            }

            return m_cur_item->payload;
        }

        T* operator->()
        {
            if (m_cur_item == NULL)
            {
                return NULL;
            }

            return &(m_cur_item->payload);
        }

    private:

        void increment()
        {
            if (m_cur_item == NULL || m_cur_item->next == NULL)
            {
                return;
            }

            m_cur_item = m_cur_item->next;
        }

        void decrement()
        {
            if (m_cur_item == NULL || m_cur_item->prev == NULL)
            {
                return;
            }

            m_cur_item = m_cur_item->prev;
        }

        LinkListElement* m_cur_item;
        static T dummy;

    };

    // Need access to internal LinkListElement pointer
    friend class iterator;

    list()
    {
        // Add sentinel to mark end of list.
        m_tail = new LinkListElement;
        m_tail->next = m_tail;
        m_tail->prev = m_tail;
        m_head = m_tail;
    }

    ~list()
    {
        // Clear entire list except for sentinel
        clear();

        // Destroy sentinel
        delete m_tail;
        m_head = NULL;
        m_tail = NULL;
    }

    T& back()
    {
        // empty list with only sentinel. Result of back() is undefined
        if (empty())
        {
            // TODO: Show some debug error
        }

        return m_tail->prev->payload;
    }

    T& front()
    {
        if (empty())
        {
            // TODO: Show some debug error
        }

        // m_head is always defined even if list is empty
        return m_head->payload;
    }

    size_t size()
    {
        return m_count;
    }

    bool empty()
    {
        // head == tail means the list is empty
        return m_head == m_tail;
    }

    iterator begin()
    {
        return iterator(m_head);
    }

    iterator end()
    {
        return iterator(m_tail);
    }

    iterator insert(iterator position, const T& payload)
    {
        // Validate position by finding it in our list
        iterator find = begin();
        while (find != end() && find != position)
        {
            ++find;
        }

        if (find == end())
        {
            // TODO: Show some debug error
            return position;
        }

        return insert_before(find.m_cur_item, payload);
    }

    void push_back(const T& payload)
    {
        insert_before(m_tail, payload);
    }

    void push_front(const T& payload)
    {
        insert_before(m_head, payload);
    }

    iterator erase(iterator position)
    {
        // Validate position by finding it in our list
        iterator find = begin();
        while (find != end() && find != position)
        {
            ++find;
        }

        if (find == end())
        {
            // TODO: Show some debug error
            return position;
        }

        return remove_at(find.m_cur_item);

    }

    //iterator erase(iterator first, iterator last);    // Implement only if needed
    void pop_back()
    {
        if (!empty())
        {
            // Don't remove the sentinel
            remove_at(m_tail->prev);
        }
    }

    void pop_front()
    {
        if (!empty())
        {
            remove_at(m_head);
        }
    }

    void remove(const T& value)
    {
        iterator iter = begin();

        while (iter != end())
        {
            iterator remove = iter++;
            if (*remove == value)
            {
                remove_at(remove.m_cur_item);
            }
        }
    }

    void clear()
    {
        while (!empty())
        {
            pop_back();
        }
    }

private:

    iterator insert_before(LinkListElement* existing, const T& payload)
    {
        // Allocate memory and save the element
        LinkListElement* new_elem = new LinkListElement;

        // For classes types (not pointer to object) this should invoke copy constructor
        new_elem->payload = payload;
        new_elem->prev = existing->prev;
        new_elem->next = existing;
        existing->prev = new_elem;
        ++m_count;

        if (existing == m_head)
        {
            m_head = new_elem;
        }

        return iterator(new_elem);
    }

    iterator remove_at(LinkListElement* to_remove)
    {
        // Allocate memory and save the element
        LinkListElement* prev = to_remove->prev;
        LinkListElement* next = to_remove->next;
        prev->next = next;
        next->prev = prev;
        --m_count;

        if (to_remove == m_head)
        {
            m_head = next;
        }

        delete to_remove;

        return iterator(prev);
    }

    LinkListElement* m_head;
    LinkListElement* m_tail;
    uint32_t         m_count;
};

template <typename T> T list<T>::iterator::dummy;

#endif

【问题讨论】:

  • 我怀疑标准库实现不会费心检查未定义的行为,并且可能不会返回一个虚拟对象来掩盖它。如果你要采取任何行动,明显地失败总比隐藏它好。
  • @shayst 删除你的实现所有STL没有的东西,比如dummy,检查无效访问等等。这些东西不是免费的——这就是 STL 没有它们的原因。
  • @DavidSchwartz 我还没有实现大多数 STL 功能,例如分配器、特征等。更重要的是,我写的代码和我们自己的 C 风格列表实现非常相似,所以我不明白为什么要花更多的代码,除非我在模板上做错了什么。
  • @shayst 它比 STL 需要更多的代码,因为它完成了 STL 没有做的事情。是的,STL 做了一些你的代码没有做的事情,但你没有使用任何这些事情,所以它们无关紧要。但是您的代码包含 STL 所没有的东西您正在使用。你为你使用的东西付费。你用得越多,你付出的就越多。
  • 特别是在模板编程中,很多源代码并没有编译成编译代码,它只是将编译器规则强加给程序员的脚手架。

标签: c++ list templates stl iar


【解决方案1】:

您的代码具有各种功能并检查 STL 没有。因此,您最终会得到更多代码是有道理的。

是的,STL 有很多您的代码没有的功能。但是您没有使用它们中的任何一个,因此它们不会出现在您的代码足迹中。 STL 是使用模板设计的,因此您无需为不使用的内容付费。

您不太可能改进 STL。如果您需要添加功能,请添加它们。您无需重新发明轮子。

【讨论】:

  • 所以你是说通过模板创建一个 int 对象列表与使用指针处理链接列表的专用代码将相差大约 200 个字节?这是模板的最低价格吗?
  • 不,这些模板(可能)是免费的。让你付出代价的都是你的额外支票。 (这就是为什么 STL 做得这么好。因为它使用模板,所以你不用为不使用的东西付费。)你也有很多小问题,请参阅我的 cmets 关于不使用placement new 或构建内部对象正确。
  • 我会改写。如果我采用 C 样式列表并使用 STL 模板一对一地重新创建它,我会增加大约 200 字节的代码占用空间。不使用任何花里胡哨的东西。只是非常基本的 push、pop、front、back、end() begin() iterator++ 和 iterator == (or !=)
  • @shayst 您是否将两者都与 -O3 和删除了调试符号进行了比较?
  • @shayst 您必须非常仔细地查看整个过程和生成的代码才能了解发生了什么。没有一件事是显而易见的。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-12-03
  • 2013-01-01
  • 2011-05-01
  • 2023-04-01
  • 1970-01-01
相关资源
最近更新 更多