Подтвердить что ты не робот

Эффективные альтернативы для выставления коллекции

В С++, какие у меня альтернативы для экспонирования коллекции, с точки зрения производительности и целостности данных?

Моя проблема в том, что я хочу вернуть внутренний список данных вызывающему, но я не хочу создавать копию. Thant оставляет меня с возвратом ссылки на список или указателем на список. Тем не менее, я не сумасшедший, чтобы позволить вызывающему изменить данные, я просто хочу, чтобы он читал данные.

  • Должен ли я выбирать между эффективностью и целостностью данных?
  • Если это так, вообще лучше идти в одну сторону или это особенно важно для случая?
  • Существуют ли другие альтернативы?
4b9b3361

Ответ 1

Ответ RichQ - разумная методика, если вы используете массив, вектор и т.д.

Если вы используете коллекцию, которая не индексируется по порядковым значениям... или думаю, что вам может понадобиться в какой-то момент в ближайшем будущем... тогда вы можете захотеть разоблачить свой собственный тип итератора (s ) и связанные с ним методы begin()/end():

class Blah
{
public:
   typedef std::vector<mydata> mydata_collection;
   typedef myDataCollection::const_iterator mydata_const_iterator;

   // ...

   mydata_const_iterator data_begin() const 
      { return myPreciousData.begin(); }
   mydata_const_iterator data_end() const 
      { return myPreciousData.end(); }

private:
   mydata_collection  myPreciousData;
};

... который вы можете использовать обычным способом:

Blah blah;
for (Blah::mydata_const_iterator itr = blah.data_begin();
   itr != blah.data_end();
   ++itr)
{
   // ...
}

Ответ 2

Много раз вызывающий абонент хочет получить доступ только для итерации по коллекции. Возьмите страницу из книги Ruby и сделайте итерацию частным аспектом своего класса.

#include <algorithm>
#include <boost/function.hpp>

class Blah
{
  public:
     void for_each_data(const std::function<void(const mydata&)>& f) const
     {
         std::for_each(myPreciousData.begin(), myPreciousData.end(), f);
     }

  private:
     typedef std::vector<mydata> mydata_collection;
     mydata_collection  myPreciousData;
};

При таком подходе вы не публикуете ничего о своих внутренних компонентах, т.е. у вас даже есть коллекция.

Ответ 3

Может быть, что-то вроде этого?

const std::vector<mydata>& getData()
{
  return _myPrivateData;
}

Преимущество здесь в том, что оно очень, очень просто и безопасно, как вы получаете на С++. Вы можете использовать это, как предлагает RobQ, но вы ничего не можете сделать, чтобы кто-то не смог этого избежать, если вы не копируете. Здесь вам нужно будет использовать const_cast, что довольно легко заметить, если вы его ищете.

Итераторы, альтернативно, могут получить вам почти то же самое, но это сложнее. Единственное добавленное преимущество использования итераторов здесь (что я могу придумать) состоит в том, что вы можете иметь лучшую инкапсуляцию.

Ответ 4

Использование ссылки const или общего указателя поможет только в том случае, если содержимое базовой коллекции не изменяется со временем.

Рассмотрите свой дизайн. Нужно ли вызывающему пользователю видеть внутренний массив? Можете ли вы реструктурировать код, чтобы вызывающий объект указывал, что делать с массивом? Например, если вызывающий абонент намеревается выполнить поиск в массиве, может ли объект владельца сделать это?

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

Я бы порекомендовал сначала сначала перепроектировать, выбрав второе решение, оптимизируя для производительности третье (при необходимости).

Ответ 5

Одно из преимуществ решений @Shog9 и @RichQ заключается в том, что они де-пароли клиента из реализации коллекции.

Если вы решите изменить тип своей коллекции на что-то еще, ваши клиенты все равно будут работать.

Ответ 6

То, что вы хотите, доступно только для чтения, без копирования всего капли данных. У вас есть пара вариантов.

Во-первых, вы можете просто вернуть const-референс независимо от вашего контейнера данных, как это было предложено выше:

const std::vector<T>& getData() { return mData; }

Это имеет недостаток конкретности: вы не можете изменить способ хранения данных внутри, не меняя интерфейса своего класса.

Во-вторых, вы можете вернуть указательные указатели к фактическим данным:

const T* getDataAt(size_t index)
{
   return &mData[index];
}

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

Другой вариант - предоставить пару итераторов, что немного сложнее. Это имеет те же преимущества указателей, а также не обязательно (обязательно), чтобы обеспечить вызов getNumItems, и там значительно больше работы, чтобы разбить итераторы их константы.

Вероятно, самый простой способ справиться с этим - использовать диапазон увеличения:

typedef vector<T>::const_iterator range_iterator_type;
boost::iterator_range< range_iterator_type >& getDataRange()
{
    return boost::iterator_range(mData.begin(), mData.end());
}

Это имеет преимущества диапазонов, которые можно скомпилировать, фильтровать и т.д., как вы можете видеть на веб-сайте.

Ответ 7

Использование const - разумный выбор. Вы также можете проверить библиотеку ускорения С++ для реализации их общего указателя. Он предоставляет преимущества указателей, то есть у вас может быть требование вернуть общий указатель на "null", который не разрешала ссылка.

http://www.boost.org/doc/libs/1_36_0/libs/smart_ptr/smart_ptr.htm

В вашем случае вы должны запретить запись общего типа указателя const.

Ответ 8

Если у вас есть std::list простых старых данных (то, что .NET будет вызывать "типы значений" ), то возвращение ссылки const в этот список будет прекрасным (игнорируя злые вещи, такие как const_cast)

Если у вас есть std::list указателей (или boost::shared_ptr), это остановит вас только на изменение коллекции, а не на элементы коллекции. Мой С++ слишком ржавый, чтобы сообщить вам ответ на этот вопрос: - (

Ответ 9

В следующих двух статьях подробно излагаются некоторые проблемы, связанные с инкапсулированием классов контейнеров. Хотя они не обеспечивают полного отработанного решения, они по существу приводят к такому же подходу, что и Shog9.

Часть 1: Инкапсуляция и вампиры
Часть 2 (теперь требуется бесплатная регистрация): Поезд Wreck Spotting
 Кевлин Хенни

Ответ 10

Я предлагаю использовать обратные вызовы в строках EnumChildWindows. Вам нужно будет найти некоторые способы, чтобы пользователь не мог изменять ваши данные. Возможно, используйте указатель const указатель/ссылку.

С другой стороны, вы можете передать копию каждого элемента в функцию обратного вызова, переписывая копию каждый раз. (Вы не хотите генерировать копию всей своей коллекции. Я предлагаю только сделать копию по одному элементу за раз. Это не займет много времени/памяти).

MyClass tmp;
for(int i = 0; i < n; i++){
    tmp = elements[i];
    callback(tmp);
}