векторная конструкция c ++ с заданной памятью - PullRequest
6 голосов
/ 31 января 2011

Я бы хотел использовать std :: vector для управления данным фрагментом памяти.Прежде всего, я уверен, что это не очень хорошая практика, но любопытство лучше меня, и я все равно хотел бы знать, как это сделать.

Проблема, с которой я столкнулся, заключается в следующем:

vector<float> getRow(unsigned long rowIndex)
{
    float* row = _m->getRow(rowIndex); // row is now a piece of memory (of a known size) that I control
    vector<float> returnValue(row, row+_m->cols()); // construct a new vec from this data
    delete [] row; // delete the original memory 
    return returnValue; // return the new vector 
}

_m - это класс интерфейса DLL, который возвращает массив с плавающей точкой, ответственность за удаление которого несет вызывающая сторона.Поэтому я хотел бы обернуть это в вектор и вернуть его пользователю .... но эта реализация выделяет новую память для вектора, копирует ее, а затем удаляет возвращенную память, а затем возвращает вектор.

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

ОБНОВЛЕНИЕ : Первоначальная мотивация этого (память, возвращаемая из DLL) была довольно жестко подавлена ​​несколькими респондентами :) Однако я бы хотел узнать ответвопрос в любом случае ... Есть ли способ построить std :: vector, используя заданный кусок предварительно выделенного массива T * памяти и размер этой памяти?

Ответы [ 6 ]

5 голосов
/ 31 января 2011

Очевидный ответ заключается в использовании пользовательского распределителя, однако вы можете обнаружить, что это действительно довольно тяжелое решение для того, что вам нужно. Если вы хотите сделать это, самый простой способ - взять распределитель, определенный (в качестве аргумента шаблона scond по умолчанию для вектора <>) реализацией, скопировать его и заставить работать должным образом.

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

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

Комментарий к ОБНОВЛЕНИЕ : "Есть ли способ создать std :: vector, используя заданный кусок предварительно выделенного массива памяти T * и размер этой памяти?"

Конечно, простой ответ здесь - «Нет». Если вы хотите, чтобы результатом был вектор <>, тогда он должен поддерживать рост по мере необходимости, например, с помощью метода reserve (), и это не будет возможно для данного фиксированного распределения. Таким образом, настоящий вопрос действительно: чего именно вы хотите достичь? Что-то, что можно использовать, например, вектор <>, или что-то, что действительно должно в каком-то смысле быть вектором, и если да, то каков этот смысл?

4 голосов
/ 31 января 2011

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

Было бы намного проще, если бы вы могли записать непосредственно в вектор:

vector<float> getRow(unsigned long rowIndex) {
  vector<float> row (_m->cols());
  _m->getRow(rowIndex, &row[0]);  // writes _m->cols() values into &row[0]
  return row;
}

Обратите внимание, что & row [0] является плавающей точкой *, и вектор гарантированно хранит элементы непрерывно.

2 голосов
/ 31 января 2011

Здесь важно знать, что разные DLL / модули имеют разные кучи. Это означает, что любая память, выделенная из DLL, должна быть удалена из этой DLL (это не просто вопрос версии компилятора или delete против delete[] или чего-либо еще). НЕ ПРОЙДИТЕ ОТВЕТСТВЕННОСТЬ УПРАВЛЕНИЯ ПАМЯТЬЮ ПО ГРАНИЦЕ DLL. Это включает создание std::vector в dll и его возврат. Но это также включает в себя передачу std::vector в DLL для заполнения DLL; такая операция небезопасна, поскольку вы точно не знаете, что std::vector не будет пытаться изменить размер какого-либо вида, пока он заполняется значениями.

Есть два варианта:

  • Определите свой собственный allocator для класса std::vector, который использует функцию выделения, которая гарантированно находится в DLL / модуле, из которого был создан вектор. Это легко сделать с помощью динамического связывания (то есть заставить класс allocator вызвать некоторую виртуальную функцию). Так как динамическое связывание будет искать в vtable для вызова функции, гарантируется, что оно попадет в код из DLL / модуля, который изначально его создал.

  • Не передавайте векторный объект в или из DLL. Например, вы можете использовать функцию getRowBegin() и getRowEnd(), которая возвращает итераторы (то есть указатели) в массиве строк (если он является смежным), и позволить пользователю std::copy преобразовать его в свой локальный std::vector объект. Вы также можете сделать это наоборот, передать итераторы begin () и end () функции, подобной fillRowInto(begin, end).

Эта проблема очень реальна, хотя многие люди пренебрегают ею, не зная. Не стоит недооценивать это. Я лично страдал от тихих ошибок, связанных с этой проблемой, и это было не красиво! Мне потребовались месяцы, чтобы решить это.

Я проверил в исходном коде, и boost::shared_ptr и boost::shared_array используют динамическое связывание (первый вариант выше), чтобы справиться с этим .. однако, они не гарантированно будут двоично-совместимыми. Тем не менее, это может быть немного лучшим вариантом (обычно двоичная совместимость является гораздо меньшей проблемой, чем управление памятью между модулями).

0 голосов
/ 31 января 2011

Библиотека Boost.SmartPtr содержит множество интересных классов, некоторые из которых предназначены для работы с массивами.

Например, вот scoped_array :

int main(int argc, char* argv[])
{
  boost::scoped_array<float> array(_m->getRow(atoi(argv[1])));
  return 0;
}

Проблема, конечно, в том, что scoped_array нельзя скопировать, поэтому, если вы действительно хотите std::vector<float>, @Fred Nurk's, вероятно, лучшее, что вы можете получить.

В идеальном случае вам нужен эквивалент unique_ptr, но в виде массива, однако я не думаю, что это является частью стандарта.

0 голосов
/ 31 января 2011

Если вы пытаетесь изменить, где / как вектор выделяет / перераспределяет / освобождает память, параметр шаблона распределителя для векторного класса - это то, что вы ищете.

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

Звучит так, будто вы ищете форму умного указателя. Тот, который удаляет то, на что он указывает, когда он уничтожен. Изучите библиотеки Boost или накатайте свои собственные в этом случае.

0 голосов
/ 31 января 2011

Ваша лучшая ставка, вероятно, std::vector<shared_ptr<MatrixCelType>>.

Много подробностей в этой теме .

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