Лучшие практики работы с указателями в функциях C ++ со структурами - PullRequest
0 голосов
/ 20 апреля 2020

Я начинаю с C++ программирования и считаю, что у меня есть гр asp по поводу указателей. Однако я пытаюсь понять лучшие практики для указателей и функций при использовании распорок.

Пример кода игрушки

Ниже приведен пример кода Toy, чтобы проиллюстрировать два способа выполнения одной и той же вещи:

#include <stdio.h>

struct rectangle {
  int width;
  int length;
};

void printRect(rectangle rect) {
  printf("Rectangle: width=%d, length=%d\n",rect.width, rect.length);
}

void doubleSizeRectangle_1(rectangle *rect) {
  rect->width = rect->width*2;
  rect->length = rect->length*2;
}

rectangle doubleSizeRectangle_2(rectangle rect) {
  rectangle *r = &rect;
  r->width = r->width*2;
  r->length = r->length*2;
  return *r;
}

rectangle doubleSizeRectangle_3(rectangle rect) {
  rect.width = rect.width*2;
  rect.length = rect.length*2;
  return rect;
}

int main()
{
  rectangle rect;
  rect.width = 2;
  rect.length = 5;

  rectangle *rect_pointer = new rectangle;
  rect_pointer = &rect;

  printRect(rect);
  printRect(*rect_pointer);

  printf("Applying functions:\n");
  doubleSizeRectangle_1(rect_pointer);
  printRect(rect);

  rect = doubleSizeRectangle_2(*rect_pointer);
  printRect(rect);

  rect = doubleSizeRectangle_3(*rect_pointer);
  printRect(rect);
}

Этот код возвращает следующий вывод:

Rectangle: width=2, length=5
Rectangle: width=2, length=5
Applying functions:
Rectangle: width=4, length=10
Rectangle: width=8, length=20
Rectangle: width=16, length=40

Первые два отпечатка предназначены только для проверки использования указателей.

Остальные отпечатки предназначены для проверки трех функций doubleSizeRectangle_1, doubleSizeRectangle_2 и doubleSizeRectangle_3, которые выполняют одни и те же действия разными способами. Первый возвращает void и использует указатель в качестве входных данных, тогда как второй и третий имеют переменную в качестве входных данных и возвращают прямоугольник struct. Третий вариант кажется лучше второго, но хотел бы подтвердить. Я не уверен насчет первого по сравнению с остальными.

Вопрос

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

Ответы [ 3 ]

2 голосов
/ 20 апреля 2020

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

Не используйте new. Не используйте указатели, когда в этом нет необходимости. Предпочитайте ссылки указателям, когда nullptr не является допустимым параметром (в вашем случае это не так). Используйте ссылки const, чтобы избежать копий:

#include <stdio.h>

struct rectangle {
  int width;
  int lenght;
};

void printRect(const rectangle& rect) {
  printf("Rectangle: width=%d, lenght=%d\n",rect.width, rect.lenght);
}


void doubleSizeRectangle_3(rectangle& rect) {
  rect.width = rect.width*2;
  rect.lenght = rect.lenght*2;
}

int main()
{
  rectangle rect;
  rect.width = 2;
  rect.lenght = 5;

  doubleSizeRectangle_3(rect);
  printRect(rect);
}

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

Вы также должны использовать конструктор для инициализации членов структур и предпочитать безопасный тип C ++ - IO (std::cout) над не типизированным C -IO.

Для дальнейшего чтения: Почему программисты C ++ должны минимизировать использование 'new'?

1 голос
/ 20 апреля 2020

Я бы вообще не использовал указатели для функции, которая редактирует структуру прямоугольника:

void doubleSizeRectangle(rectangle& rect) 
{
     rect.width = rect.width*2;
     rect.lenght = rect.lenght*2;
}

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

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

doubleSizeRectangle_2 не имеет смысла. Вы передаете свою структуру по значению, что означает, что ваша функция получает ее копию. Затем вы создаете указатель на этот объект, манипулируете им через этот указатель, чтобы вернуть его как значение снова. Использование указателя здесь бесполезно.

doubleSizeRectangle_3 - это просто doubleSizeRectangle_2 без странного указателя. Я бы не использовал это, он делает по крайней мере одну копию (при передаче в функцию) и одну операцию перемещения (возвращающую структуру из функции), которая не обязательна для функции, которая просто хочет отредактировать структуру. Придерживайтесь моего предложения или вашего doubleSizeRectangle_1.

1 голос
/ 20 апреля 2020

Я предлагаю вам не делать так:

rectangle doubleSizeRectangle_2(rectangle rect) {
  rectangle *r = &rect;
  r->width = r->width*2;
  r->lenght = r->lenght*2;
  return *r;
}

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

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

std::unique_ptr<rectangle> smartptrToRectangle(std::make_unique<rectangle>());

, умный указатель - это класс, который реализует идиому RAII:

Объяснение RAII

умный указатель уничтожает элемент, как только он go выходит из области видимости.

...