Универсальный метод для установки длины динамического массива произвольного типа в c ++ - PullRequest
1 голос
/ 11 августа 2009

Я делаю проект по преобразованию некоторого кода Pascal (Delphi) в C ++ и хотел бы написать функцию, которая примерно эквивалентна методу Pascal "SetLength". Это берет ссылку на динамический массив, а также длину и выделяет память и возвращает ссылку.

В C ++ я думал о чем-то вроде

void* setlength(void* pp, int array_size, int pointer_size, int target_size, ....) {
      void * p;
      // Code to allocate memory here via malloc/new
      // something like:   p = reinterpret_cast<typeid(pp)>(p); 
      //                   p=(target_size) malloc(array_size);
      return p;
}

Мой вопрос таков: есть ли способ передать тип указателя в функцию, подобную этой, и успешно выделить память (возможно, с помощью параметра typeid?)? Могу ли я использовать

<reinterpret_cast>

как-то? Конечной целью было бы что-то вроде следующего с точки зрения использования:

float*** p;
p=setlength(100,sizeof(float***),sizeof(float**),.....);

class B;
B** cp;
cp=setlength(100,sizeof(B**),sizeof(B*),.....);

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

Ответы [ 5 ]

5 голосов
/ 11 августа 2009

Используйте std :: vector вместо необработанных массивов. Затем вы можете просто вызвать его resize() метод-член.

И сделайте функцию шаблоном для обработки произвольных типов:

Если вы хотите использовать свою функцию, она может выглядеть примерно так:

template <typename T>
std::vector<T>& setlength(std::vector<T>& v, int new_size) {
      v.resize(new_size);
      return v;
}

Но теперь это так просто, что вы можете полностью исключить функцию и просто вызвать resize для начала.

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

std::vector<float>v(100);
2 голосов
/ 11 августа 2009

Если бы вы хотели сделать это буквально, вы бы сделали это так:

template <typename T>
T* SetLength(T* arr, size_t len) {
    return static_cast<T*>(realloc(arr, sizeof(T) * len));
}

Обратите внимание, что массив должен быть выделен с malloc или calloc. Также обратите внимание, что это фактически не изменяет размер памяти - оно освобождает память и перераспределяет память соответствующего размера. Если были какие-либо другие указатели на передаваемый массив, они впоследствии будут недействительными.

Вам действительно лучше использовать более идиоматическое решение C ++, например std::vector.

1 голос
/ 11 августа 2009

Для многомерного массива, вероятно, наилучшим вариантом будет использование библиотеки multi_array boost:

typedef boost::multi_array<float, 3> array_type;
array_type p(boost::extents[100][100][100]); // make an 100x100x100 array of floats
p[1][2][3] = 4.2;

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

В противном случае у вас есть три других основных варианта.

Самый вариант C ++ -y без использования внешних библиотек - использовать контейнер STL:

std::vector<float **> p;
p.resize(100);

Как и в случае multi_array, p будет автоматически освобожден при выходе из области видимости. Вы можете получить векторные границы с p.size(). Однако вектор будет обрабатывать только одно измерение для вас, поэтому вы в конечном итоге будете выполнять вложенные векторы (ick!).

Вы также можете использовать new напрямую:

float ***p = new float**[100];

Для освобождения:

delete [] p;

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

Приведенные выше три метода вызовут исключение типа std::bad_alloc, если им не удастся выделить достаточно памяти.

Наконец, для полноты, есть маршрут C, с calloc():

float ***p = (float ***)calloc(100, sizeof(*p));

бесплатно:

free((void*)p);

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

Если calloc() не выделяет память, возвращается NULL; вам нужно проверить это и разобраться с этим.

0 голосов
/ 11 августа 2009

Чтобы сделать это C ++:

1) Как указано в jalf, предпочитайте std :: vector, если можете
2) Не делайте void * p.Вместо этого предпочтите сделать вашу функцию шаблоном типа T.

0 голосов
/ 11 августа 2009

Новый оператор, по сути, является тем, о чем вы просите, за исключением того, что для правильного размещения двойных / тройных указателей вы должны сделать что-то вроде следующего:

float** data = new float*[size_of_dimension_1];
for ( size_t i=0 ; i<size_of_dimension_1 ; ++i )
    data[i] = new float[size_of_dimension_2];

...

// to delete:
for ( size_t i=0 ; i<size_of_dimension_1 ; ++i )
    delete [] data[i];
delete [] data;

Редактировать: Я бы предложил использовать одну из множества математических / матричных библиотек C ++. Я бы предложил uBlas .

...