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

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

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

  • Придется ли мне выбирать между производительностью и целостностью данных?
  • Если да, то в целом лучше пойти в одну сторону или это конкретно в конкретном случае?
  • Есть ли другие альтернативы?
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
12
0
632
10
Перейти к ответу Данный вопрос помечен как решенный

Ответы 10

Может как то так?

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

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

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

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

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

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

Ответ принят как подходящий

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

Если вы используете коллекцию, которая не индексируется порядковыми значениями ... или думаете, что вы может потребоваться в какой-то момент в ближайшем будущем ... тогда вы можете рассмотреть возможность раскрытия своего собственного типа (ов) итератора и связанного с ним 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)
{
   // ...
}

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

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

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

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

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

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

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

Во-первых, вы можете просто вернуть константную ссылку на ваш контейнер данных, как предложено выше:

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

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

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

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

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

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

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

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

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

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

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

Часто вызывающий хочет получить доступ просто для перебора коллекции. Возьмите страницу из книги 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;
};

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

Я знаю, что это старый ответ, но теперь я делаю это с помощью std :: function: void for_each_data(const std::function<void(const mydata&)>& f) const;

jbatez 11.10.2013 23:39

@JoBates Я больше не использую C++ изо дня в день, пожалуйста, не стесняйтесь редактировать мой ответ, чтобы обновить его!

David Joyner 16.10.2013 16:41

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

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

К вашему сведению, ссылки, которые вы разместили, мертвы.

MM. 20.05.2016 01:27

@ ММ. Спасибо за уведомление, теперь исправлено.

Skeve 08.02.2017 11:25

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

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

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

Другие вопросы по теме