Прототип для функции, которая выделяет память на "куче" (C/C++)

Без сомнения...

State machines

6
задан Joe Snikeris 23 July 2009 в 13:55
поделиться

12 ответов

В C, это было бы более или менее законно.

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

И теперь вы можете сказать: «Как бы это утечка памяти, я вызываю delete [] прямо здесь! ", но что, если возникнет исключение в // ... lines?

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

std::vector<char> f();

std::vector<char> data = f();
int data_length = data.size();
// ...
//delete[] data; 

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

I в ответ на комментарии следует упомянуть, что вышесказанное подразумевает копию вектора, что потенциально может быть дорогостоящим. Большинство компиляторов, если функция f не слишком сложна, оптимизируют эту копию, так что это будет нормально. (и если функция вызывается не слишком часто, накладные расходы все равно не будут иметь значения ). Но если этого не произойдет, вы можете вместо этого передать пустой массив в функцию f по ссылке, а f сохранить свои данные в нем вместо возврата нового вектора.

Если выполнение возврата копии неприемлемо, другой альтернативой было бы полностью разделить выбор контейнера и вместо этого использовать итераторы:

// definition of f
template <typename iter>
void f(iter out);

// use of f
std::vector<char> vec;
f(std::back_inserter(vec));

Теперь можно использовать обычные операции итератора ( * out для ссылки или записи в текущий элемент и ++ out для перемещения итератора к следующему элементу) - и, что более важно, теперь будут работать все стандартные алгоритмы. Вы можете использовать std :: copy , например, для копирования данных в итератор. Это подход, который обычно выбирается стандартной библиотекой (т.е. это хорошая идея;)), когда функция должна возвращать последовательность данных.

Другой вариант - сделать ваш собственный объект ответственным за распределение / deallocation:

struct f { // simplified for the sake of example. In the real world, it should be given a proper copy constructor + assignment operator, or they should be made inaccessible to avoid copying the object
  f(){
    // do whatever the f function was originally meant to do here
    size = ???
    data = new char[size];
  }
  ~f() { delete[] data; }

int size;
char* data;
};

f data;
int data_length = data.size;
// ...
//delete[] data; 

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

Общее практическое правило C ++ таково: «если вы пишете delete или delete [] вне объект RAII, вы делаете это неправильно. Если вы пишете new или `new [] вне объекта RAII, вы делаете это неправильно, если только результат не передается немедленно в интеллектуальный указатель "

повторяя запись delete или delete [] вне объекта RAII, вы делаете это неправильно. Если вы пишете new или `new [] вне объекта RAII, вы делаете это неправильно, если только результат не передается немедленно в интеллектуальный указатель»

повторяя запись delete или delete [] вне объекта RAII, вы делаете это неправильно. Если вы пишете new или `new [] вне объекта RAII, вы делаете это неправильно, если только результат не передается немедленно в интеллектуальный указатель»

6
ответ дан 8 December 2019 в 12:22
поделиться

Ваша функция не должна возвращать голый указатель на некоторую память. Указатель ведь можно скопировать. Тогда возникает проблема владения: кто на самом деле владеет памятью и должен ее удалить? У вас также есть проблема, что голый указатель может указывать на один объект в стеке, в куче или на статический объект. Он также может указывать на массив в этих местах. Учитывая, что вы возвращаете только указатель, как пользователи должны знать?

Вместо этого вы должны вернуть объект, который соответствующим образом управляет своим ресурсом. (Найдите RAII.) Укажите тот факт, что ресурс в этом случае представляет собой массив из char , либо std :: string , либо std :: vector ] кажутся лучшими:

int f(std::vector<char>& buffer);

std::vector<char> buffer;
int result = f(buffer);
4
ответ дан 8 December 2019 в 12:22
поделиться

В «правильном» C ++ вы должны вернуть объект, содержащий выделение памяти где-то внутри него. Что-то вроде std :: vector.

5
ответ дан 8 December 2019 в 12:22
поделиться

Почему бы не сделать то же самое, что и malloc () - void * malloc (size_t numberOfBytes)? Таким образом, количество байтов является входным параметром, а выделенный адрес блока - возвращаемым значением.

UPD: В комментариях вы говорите, что f () в основном выполняет некоторые действия помимо выделения памяти. В этом случае использование std :: vector - намного лучший способ.

void f( std::vector<char>& buffer )
{
    buffer.clear();
    // generate data and add it to the vector
}

вызывающий будет просто передать выделенный вектор:

 std::vector buffer;
 f( buffer );
 //f.size() now will return the number of elements to work with
3
ответ дан 8 December 2019 в 12:22
поделиться

Передайте указатель по ссылке ...

int f(char* &buffer)

Однако вы можете рассмотреть возможность использования указателей с подсчетом ссылок, таких как boost :: shared_array, для управления памятью, если вы просто начиная с этого.

например,

int f(boost::shared_array<char> &buffer)
2
ответ дан 8 December 2019 в 12:22
поделиться

Используйте шаблон проектирования RAII (получение ресурсов - инициализация).

http: //en.wikipedia.org/wiki/RAII Понимание значения термина и концепции - RAII (получение ресурсов - это инициализация)

1
ответ дан 8 December 2019 в 12:22
поделиться

Просто верните указатель:

char * f() {
   return new char[100];
}

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

0
ответ дан 8 December 2019 в 12:22
поделиться

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

class Cookie {
public:
   Cookie () : pointer (new char[100]) {};
   ~Cookie () {
      delete[] pointer;
   }
private:
   char * pointer;

   // Prevent copying. Otherwise we have to make these "smart" to prevent 
   // destruction issues.
   Cookie(const Cookie&);
   Cookie& operator=(const Cookie&);
};
0
ответ дан 8 December 2019 в 12:22
поделиться

Если все f () делает с буфером, чтобы вернуть его (и его длину), пусть он просто вернет длину, и вызовет new его. Если f () также что-то делает с буфером, то действуйте так, как предполагалось полиглотом.

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

0
ответ дан 8 December 2019 в 12:22
поделиться

Правильный стиль, вероятно, состоит в использовании не char *, а std :: vector или std :: string в зависимости от того, для чего вы используете char *.

О проблеме передача параметра, который необходимо изменить, вместо передачи указателя передайте ссылку. В вашем случае:

int f(char*&);

и если вы последуете первому совету:

int f(std::string&);

или

int f(std::vector<char>&);
0
ответ дан 8 December 2019 в 12:22
поделиться

Я думаю, вы пытаетесь выделить одномерный массив. Если это так, вам не нужно передавать указатель на указатель.

int f(char* &buffer)

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

char* data;
int data_length = f(data);
// ...
delete[] data;
-2
ответ дан 8 December 2019 в 12:22
поделиться

При условии, что f выполняет new [] для сопоставления, он будет работать, но это не очень идиоматично.

Предполагая, что заполняет данные, а не просто malloc () - похоже, вам лучше обернуть распределение как std :: vector

void f(std::vector<char> &buffer)
{
    // compute length
    int len = ...

    std::vector<char> data(len);

    // fill in data
    ...

    buffer.swap(data);
}

EDIT - удалить ложный * из подписи

0
ответ дан 8 December 2019 в 12:22
поделиться