Например, у нас есть функция, результатом которой является массив. Я часто вижу что-то вроде этого:

void func(some_args, result_array){//some code//} 

Как видите, функция не возвращает массив, а мы передаем его в качестве аргумента.

Второй случай выглядит так:

float* func(some_args){ //some code// return result_array;}

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

UPD : извините за неточность. Конечно, во втором случае я подразумевал указатель на массив.

c++
4
Max 8 Май 2016 в 23:09

3 ответа

Лучший ответ

Разница в собственности.

void func(some_args, float*);

Против

float* func(some_args);

В первой форме очень ясно, кто отвечает за предоставление памяти для массива. Во второй причине возникает двусмысленность: принадлежит ли функция, которую вы назвали, памяти или она передается вам. Кто несет ответственность за это delete []?

char* s = strdup("hello");
// I have to remember to 'free()' what was strdup'd
// but what if I think "this is C++" and delete[] it?

-- Редактировать --

Возможно, это частично способствовало эволюции интеллектуальных указателей C ++ 11 (std::unique_ptr и std::shared_ptr), поэтому лучшим вариантом, чем два обсуждаемых, было бы использование одного из них.

Std :: unique_ptr func (some_args);

В нем прямо говорится: «Я верну вам то, за что вы будете нести ответственность».

4
kfsone 8 Май 2016 в 21:03

В системе типов C ++ массивы - это граждане второго сорта.

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

Проблема массивов в том, что

  • Они в большинстве случаев «распадаются» на указатель на первый элемент, теряя информацию о размере.
  • Размер массива - это константа времени компиляции

std::vector - это тонкая оболочка для динамически выделяемого массива, но ее можно передавать функциям или манипулировать ими, как и любым другим типом значения, а также размер является значением времени выполнения. Конечно, не существует золотого правила, которое можно применять всегда ... но чаще всего, когда в C вы используете массив, в C ++ вместо этого используется std::vector.

2
6502 8 Май 2016 в 21:38

есть ли какое-то соглашение об этом

Да, для этого вопроса существует «условность».

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

Например, «Руководство по стилю Google C ++» (угадайте, насколько легко его найти) определяет порядок параметров:

При определении функции порядок параметров следующий: входы, затем выходы.

Параметры функций C / C ++ либо вводятся в функцию, либо выводятся из функции, либо и то, и другое. Входные параметры обычно являются значениями или константными ссылками, тогда как выходные и входные / выходные параметры будут неконстантными указателями. При заказе параметров функции помещайте все параметры только для ввода перед любыми параметрами вывода. В частности, не добавляйте новые параметры в конец функции только потому, что они новые; поместите новые параметры только для ввода перед параметрами вывода.

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

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


Вы неправильно представляете свой первый случай:

void func(some_args, result_array){//some code//} 

Итак, позвольте мне попробовать презентацию с дополнительными подсказками:

Соглашение, обсуждаемое в Руководстве по стилю Google C ++, лучше было бы представить как

void func(T1 argIn1, 
          T2 argIn2, 
          const T3& argIn3,
          std::vector<T4>& argOut4);

Таким образом, argIn1 и argIn2 передаются в func по значению и явно вводятся.

ArgIn3 - это const ref, а также вход. Вот как передать большие входные данные в функцию без затрат на копирование.

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


Существует множество примеров кода (например, linux api и т. Д.), В которых функция что-то возвращает. Вы можете сами посчитать и решить, применим ли какой-либо результат к вашим усилиям. Я могу найти множество (например, memcmp), где возвращаемое значение является важным результатом, но у меня есть ощущение (что я не подтвердил), что большинство (или, возможно, больше) исторических функций возвращают статус ... возможно, 0 в случае успеха или -1 при ошибке и значение в errno для описания.

Таким образом, в наши дни я предпочитаю возвращать std :: string. Когда возвращаемый size () равен 0, ошибки не возникает, иначе описание ошибки фиксируется в строке.


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

Следование руководству по стилю (любому стандарту кодирования) - это хорошо. Я слежу за несколькими частями руководства по стилю Google уже несколько лет. Он легко соединился с моей стандартной практикой.

1
2785528 8 Май 2016 в 20:59