0

I'm starting with C++ programming and I believe I have got a grasp about pointers. However I'm trying to understand the best practices for pointers and functions while using struts.

Toy example code

I have made below Toy example code to exemplify two ways to perform same thing:

#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);
}

That code returns following output:

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

The first two prints are just to check about pointers usage.

The remaining prints are to check the three functions doubleSizeRectangle_1, doubleSizeRectangle_2 and doubleSizeRectangle_3 that perform same actions in different ways. The first one returns void and uses a pointer as input, whereas the second and third one have a variable as input and return a rectangle struct. Third option seems better than second, but would like to confirm. I'm not sure about the first one compared to the rest.

Question

Which option would be better in terms of best practice and why? Is there any of these options better in terms of avoiding memory leaks? May there be any other ways using pointers, and would those be even better than those I posted?

Cedric Zoppolo
  • 3,077
  • 4
  • 25
  • 40
  • 2
    Why use pointers instead of references? – David Schwartz Apr 20 '20 at 06:39
  • 1
    You forgot `void doubleSizeRectangle_ref(rectangle & rect) ` – Richard Critten Apr 20 '20 at 06:39
  • @DavidSchwartz, well that's a good question. I have searched also about when it's useful to use pointers instead of references, and I'm still not sure how to decide about it. But as I'm trying to learn about managing pointers I thought this would be a good example. Maybe there is a better example where you can only go with pointers and no references, but I don't know any. – Cedric Zoppolo Apr 20 '20 at 06:43
  • @RichardCritten, be my guest on adding it to an answer. Not sure how I would implement it and it may be better even than those I have made. – Cedric Zoppolo Apr 20 '20 at 06:44
  • @RichardCritten, I rephrased my question so you can add your answer ;) – Cedric Zoppolo Apr 20 '20 at 06:46
  • 1
    Use a pointer if you want to handle the possible case that there is non object (with `nullptr`). If you want to achieve that object existence is granted then use reference instead. – Scheff's Cat Apr 20 '20 at 06:47
  • 1
    Either option 3 or option 4 `rectangle doubleSizeRectangle_3(rectangle& rect)`. The main reason being is it means that you as the function writer get to assume the validity of the incoming data. You'll never need `if (rect != nullptr)` or `assert(rect != nullptr)` and or some documentation note. In general I make the choice of passing by reference or value pretty abrirtrarily, I'll default to by const reference but if there's only say 1-16 bytes by eye in whatever I'm passing, I might switch to value. Also your code does leak memory, remember the rule, you need one delete for every new. – George Apr 20 '20 at 06:50

3 Answers3

2

"best" is rather subjective, but using something complicated when there is no need to is not "best" in any sense.

Don't use new. Don't use pointers when there is no need to. Prefer references over pointers when nullptr is not a valid paramter (it isn't in your case). Use const references to avoid copies:

#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);
}

Your function passed a pointer and returned the modified parameter. You do not need both. I changed it to return nothing and take the paramter by reference, because passing a nullptr does not make sense here.

You should also use a constructor to initialize the structs members, and prefer the type safe C++-IO (std::cout) over the non-typesafe C-IO.

For further reading: Why should C++ programmers minimize use of 'new'?

463035818_is_not_a_number
  • 64,173
  • 8
  • 58
  • 126
1

I suggest you to avoid doing like this:

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

you don't need to access that parameter using a pointer inside the method, you could simply access to object, because it's a copy.

As best practice, in order to minimize the risk of memory leak, the suggestion is to use smart pointers like this:

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

a smart pointer is a class that implements the RAII idiom:

RAII explanation

a smart pointer does destroy the element for you as soon as it go out of scope.

SeventhSon84
  • 364
  • 1
  • 4
  • No ownership transfer, input parameter should not be smart pointer. Return type is questionable (NRVO, struct size, simple to copy) but acceptable. – Jarod42 Apr 20 '20 at 07:09
1

I would not use pointers for a function that edits a rectangle struct at all:

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

The main difference between passing a pointer and a reference is just that a pointer can be unassigned while a reference always references a rectangle struct.

Your doubleSizeRectangle_1 essentially does the same thing. You should consider checking if the pointer is assigned if you want to stick to using pointers.

doubleSizeRectangle_2 makes no sense. You are passing your struct by value which means your function receives a copy of it. You then create a pointer to this object, manipulate it through that pointer to return it as a value again. The use of the pointer is useless there.

doubleSizeRectangle_3 is just doubleSizeRectangle_2 without the weird pointer. I wouldn't use this, it does at least one copy (when passing to the function) and one move operation (returning the struct from the function) that isn't nessecary for a function that just want's to edit the struct. Stick to my suggestion or your doubleSizeRectangle_1.

Eric
  • 815
  • 2
  • 11