2016-07-11 4 views
1

Я использую IAR как компилятор для встроенного проекта. Я пытаюсь представить некоторые шаблоны для базовых типов, таких как список, но каждый созданный объект списка STL увеличивает размер кода примерно на 200 байт относительно нашей текущей реализации стиля C. Я попытался реализовать небольшую часть списка STL, надеясь получить меньший размер кода, но в итоге оказался более тяжелым, чем полный список STL. Я делаю что-то ужасно неправильно в моем использовании шаблонов?Почему эта реализация списка занимает больше места, чем список stl?

P.S. Обратите внимание, что код не проверен, поэтому он может содержать драконов.

#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 
+0

Я не уверен, что я последую за тобой. Как добавление большего количества кода в мою реализацию уменьшит количество следов кода? Я только реализовал основные методы на данный момент, для целей тестирования. – shayst

+0

Вы говорите о размере кода или размере времени выполнения структуры данных списка? – Arunmu

+0

С одной стороны, вы по умолчанию строите 'T', а затем назначаете ему. Это может привести к чему-то другому. Вы должны использовать новое место или, по крайней мере, разумный конструктор для вашей структуры LinkListElement. –

ответ

3

У вас есть все функции и проверки, которые не имеют STL. Поэтому имеет смысл, что вы получите больше кода.

Да, у STL есть много функций, которых нет у вашего кода. Но вы не используете ни одного из них, поэтому они не отображаются в вашем значении кода. STL разработан с использованием шаблонов, поэтому вы не платите за то, что не используете.

Вряд ли вы можете улучшить STL. Если вам нужно добавить функции, добавьте их. Вам не нужно изобретать велосипед.

+0

Итак, вы говорите, что просто создание списка объекта int по шаблону против специализированного кода для обработки списка ссылок с указателями будет отличаться примерно на 200 байтов? Это минимальная цена шаблонов? – shayst

+0

Нет, шаблоны (возможно) бесплатны. Это все ваши дополнительные проверки, которые стоят вам. (Вот почему STL делает это хорошо. Поскольку он использует шаблоны, вы не платите за то, что не используете.) У вас также много мелких проблем, см. Мои комментарии о том, что вы не используете новое место или не строите внутреннюю объектов правильно. –

+0

Я буду перефразировать. Если я возьму список стилей C и воссоздаю его с помощью шаблона STL один к одному, я получаю увеличенный размер кода около 200 байт. Без использования каких-либо звонков и свистов. Просто базовый push, pop, front, back, end() begin() iterator ++ и iterator == (или! =) – shayst