Работа с размером контейнеров STL - PullRequest
3 голосов
/ 04 октября 2011

Я переписываю библиотеку общего назначения, которая была написана мной до того, как я выучил STL. Он использует массивы в стиле C полностью. Во многих местах есть такой код:

unsigned short maxbuffersize; // Maximum possible size of the buffer. Can be set by user.
unsigned short buffersize; // Current size of the buffer.
T *buffer; // The buffer itself.

Первым делом я изменил код следующим образом:

unsigned short maxbuffersize;
unsigned short buffersize;
std::vector<T> buffer;

А потом:

typedef unsigned short BufferSize;
BufferSize maxbuffersize;
BufferSize buffersize;
std::vector<T> buffer;

А потом я почувствовал, что делаю очень плохую вещь и должен пересмотреть свой стиль кодирования. Сначала BufferSize выглядел очень плохим именем для типа, но затем начали появляться всевозможные странные вопросы. Как я называю тип размера? Должен ли я использовать свой собственный тип или наследовать от std::vector<T>::size_type? Стоит ли кэшировать размер контейнера или использовать size() полностью? Должен ли я позволить пользователю вручную установить максимальный размер контейнера, а если нет, как я могу проверить переполнение?

Я знаю, что не может быть универсального подхода, поэтому я хотел бы услышать политики, используемые другими программистами и поставщиками платформ. Библиотека, над которой я работаю, является кроссплатформенной и предназначена для общего пользования и будет использоваться десятилетиями. Спасибо.

Ответы [ 3 ]

12 голосов
/ 04 октября 2011

Я думаю, что выбором по умолчанию должно быть избавление от buffersize и maxbuffersize и использование buffer.size() и buffer.capacity() во всем.

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

Наконец, в тех местах, где вы считаете, что проверка границ оправдана, вы можете использовать buffer.at(i). Это вызовет исключение, если i выходит за пределы.

2 голосов
/ 04 октября 2011

В целом, я бы посоветовал использовать итераторы для доступа к вашим данным. Когда вы делаете это, вы часто вообще не называете размер контейнера. Это также освобождает вас от использования std::vector все вместе - и позволяет вам просто перейти на, например, std::list, если позже вы поймете, что это лучше соответствует вашим потребностям.

При использовании итераторов потребность в vector.size() в целом значительно уменьшается. (когда вам это нужно, используйте buffer.size() и buffer.capacity(), как говорит Экс).

Например:

typedef unsigned short BufferSize;
BufferSize maxbuffersize;
BufferSize buffersize;
std::vector<T> buffer;
for(unsigned short i = 0; i< maxbuffersize;++i)
{
  //do something with buffer[i];
}

становится

struct do_something
{
  void operator()(const T& t) 
  { 
   //do something with buffer[i]
  }
};
std::vector<T> buffer(maxbuffersize);
std::for_each(buffer.begin(), buffer.end(), do_something());

немного чище.

0 голосов
/ 04 октября 2011

Сохранение размера полезно для структур май, но это немного избыточно для массивов / векторов, так как размер гарантированно будет конечным индексом + 1. Если вы беспокоитесь о том, чтобы дойти до конца, то итерационный подход, как было упомянуто, решит эту проблему, а также большинство других проблем, связанных с возможными размерами для сравнений и т. Д .;

довольно стандартно определять все ваши типы и их размеры в заголовке с помощью API, который устанавливает их для разных платформ и компиляторов ... посмотрите на окна с их определениями LONG, ULONG, DWORD и т. Д. Старый " C "соглашение состоит в том, чтобы предварять их уникальным именем или инициалами, такими как MYAPI_SIZETYPE. Это многословно, но позволяет избежать кросс-платформенной путаницы или проблем с компилятором.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...