Я могу использовать [] оператор в C++ для создания виртуальных массивов

У меня есть большая кодовая база, первоначально C портированный к C++ много лет назад, который работает в ряде больших массивов пространственных данных. Эти массивы содержат структуры, представляющие точку и треугольные объекты, которые представляют поверхностные модели. Я должен осуществить рефакторинг код, таким образом, что особенный метод, эти объекты хранятся внутренне, варьируется для определенных сценариев. Например, если точки лежат на обычной плоской сетке, я не должен хранить координаты X и Y, поскольку они могут быть вычислены на лету, как может треугольники. Точно так же я хочу использовать в своих интересах из базовых инструментов, таких как STXXL для устройства хранения данных. Самый простой способ сделать это заменяет доступ к массиву помещенным, и получите функции типа, например.

point[i].x = XV;

становится

Point p = GetPoint(i);
p.x = XV;
PutPoint(i,p);

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

point *p = point + i;

может не использоваться. Действительно ли этот класс выполним записать? Например, при записи методов ниже с точки зрения [] оператор;

void MyClass::PutPoint(int Index, Point p)
{
   if (m_StorageStrategy == RegularGrid)
   {
      int xoffs,yoffs;
      ComputeGridFromIndex(Index,xoffs,yoffs);
      StoreGridPoint(xoffs,yoffs,p.z);
    } else
       m_PointArray[Index] = p;   
  }
}

Point MyClass::GetPoint(int Index)
{
   if (m_StorageStrategy == RegularGrid)
   {
      int xoffs,yoffs;
      ComputeGridFromIndex(Index,xoffs,yoffs);
      return GetGridPoint(xoffs,yoffs);   // GetGridPoint returns Point
    } else
       return m_PointArray[Index];   
  }
}

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

point[a].z = point[b].z + point[c].z

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

6
задан SmacL 11 May 2010 в 09:05
поделиться

4 ответа

Прочитав приведенные выше ответы, я решил, что ответ Пита с двумя версиями оператора [] был лучшим путем вперед. Чтобы обрабатывать преобразование между типами во время выполнения, я создал новый класс шаблона массива, который принял четыре параметра следующим образом:

template<class TYPE, class ARG_TYPE,class BASE_TYPE, class BASE_ARG_TYPE>
class CMorphArray 
{
int GetSize() { return m_BaseData.GetSize(); }
BOOL IsEmpty() { return m_BaseData.IsEmpty(); }

// Accessing elements
const TYPE& GetAt(int nIndex) const;
TYPE& GetAt(int nIndex);
void SetAt(int nIndex, ARG_TYPE newElement);
const TYPE& ElementAt(int nIndex) const;
TYPE& ElementAt(int nIndex);

// Potentially growing the array
int Add(ARG_TYPE newElement);

// overloaded operator helpers
const TYPE& operator[](int nIndex) const;
TYPE& operator[](int nIndex);

   CBigArray<BASE_TYPE, BASE_ARG_TYPE>  m_BaseData;
private:
   CBigArray<TYPE, ARG_TYPE>    m_RefCache;
   CBigArray<int, int&> m_RefIndex;
   CBigArray<int, int&> m_CacheIndex;

   virtual void Convert(BASE_TYPE,ARG_TYPE) = 0;
   virtual void Convert(TYPE,BASE_ARG_TYPE) = 0;

   void InitCache();
   TYPE&    GetCachedElement(int nIndex);
};

Основное хранилище данных находится в m_BaseData , который является данными в его собственном формате, который могут различаться по типу, как описано. m_RefCache - вторичный массив для кэширования элементов в ожидаемом формате, а функция GetCachedElement использует виртуальные функции Convert для преобразования данных по мере их перемещения и из кеша. Размер кеша должен быть не меньше количества одновременных ссылок, которые могут быть активны в любой момент времени, но в моем случае, вероятно, будет лучше, если он будет больше, поскольку он уменьшает количество требуемых преобразований. Хотя реализация курсора Alsk, вероятно, сработала бы хорошо, данное решение требует меньшего количества копий объектов и временных переменных и должно обеспечивать немного лучшую производительность, что в данном случае важно.

Приношу свои извинения всем поклонникам STL за внешний вид старого MFC; остальная часть проекта - это MFC, поэтому в данном случае это имеет больше смысла.CBigArray был результатом вопроса о переполнении стека , который стал основой моей обработки больших массивов. Надеюсь закончить реализацию сегодня и протестировать завтра. Если все это пойдет мне на пользу, я соответствующим образом отредактирую этот пост.

0
ответ дан 16 December 2019 в 21:36
поделиться

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

Следующий фрагмент кода пытается обрисовать решение:

class PointVector
{
  MyClass container_;

  public:
  class PointExSet: public Point
  {
    MyClass &container_;
    int index_;

    public:
    PointExSet(MyClass &container, int index)
      :Point(container.GetVector(index)),container_(container),index_(index)
    {
    }

    ~PointExSet()
    {
      container_.PutVector(index_) = *this;
    }
  };

  PointExSet operator [] (int i)
  {
    return PointExSet(container_,i);
  }
};

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

2
ответ дан 16 December 2019 в 21:36
поделиться

Вам не нужно передавать массив по значению. Для изменения значений в массиве вам нужны две версии operator[], одна из которых возвращает ссылку (для изменения), а другая - const ссылку.

В принципе, нет причин не использовать operator[], если вам не нужно изменять тип хранилища во время выполнения - виртуальных операторов не существует, поэтому вам понадобится именованная функция, если вы хотите получить полиморфизм во время выполнения. В этом случае можно создать простую struct, которая адаптирует вызовы операторов к вызовам функций (хотя это зависит от API хранилища - если код предполагает, что присвоение переменным-членам точки изменяет хранимые данные, возможно, придется сделать тип точки шаблонной переменной, чтобы это можно было отменить).

Если посмотреть на ваш пример кода, то в нем есть тест для стратегии хранения. Не делайте этого. Либо используйте ОО и пусть ваш объект хранения реализует общий виртуальный интерфейс, либо (что, вероятно, лучше) используйте программирование шаблонов для изменения механизма хранения.

Если вы посмотрите на гарантии, предоставляемые std::vector (в более современных стандартах C++), то можно иметь что-то, что имеет динамическое хранение и позволяет использовать арифметику указателей, хотя это требует непрерывного хранения. Учитывая, что некоторые из ваших значений создаются на лету, вероятно, не стоит накладывать это ограничение на ваши реализации, но само ограничение не препятствует использованию operator[].

5
ответ дан 16 December 2019 в 21:36
поделиться

Чтобы иметь полный контроль над операциями с массивом, operator [] должен возвращать специальный объект (давно изобретенный и названный «курсором»), который будет обрабатывать операции за вас. Например:

class Container
{
  PointCursor operator [] (int i)
  {
    return PointCursor(this,i);
  }
};
class PointCursor
{
public:
    PointCursor(_container, _i)
       : container(_container), i(_i),
         //initialize subcursor
         x(container, i) {}     

    //subcursor
    XCursor x;
private:
   Container* container;
   int i;
};
class XCursor
{
public:
    XCursor(_container, _i)
      : container(_container), i(_i) {}

     XCursor& operator = (const XCursor& xc)
     {
          container[i].x = xc.container[xc.i].x;
          //or do whatever you want over x
     }

     Container* container;
     int i; 
}
//usage
my_container[i].x = their_container[j].x; //calls XCursor::operator = ()
1
ответ дан 16 December 2019 в 21:36
поделиться
Другие вопросы по тегам:

Похожие вопросы: