This article deals with getters and setters in C++. I am sorry, it is not about coroutine, but part 2 of thread pools will come in the following weeks.
TL;DR: Getters and setters are bad for structure like objects.
In this article, I will only give my opinion about them, I don’t want to offend anyone, I am just going to explain why, or why not, use getters and setters. I would be glad to have any debates in the comments part.
Just to be clear about what I am talking about when I talk about getter, I talk about a function that just returns something, and when I talk about setter, I talk about a function that just modifies one internal value, not doing any verification or other computations.
Let’s say we have a simple structure with their usual getter and setters:
return"Hello, my name is "+person.getFirstName()+" "+person.getLastName()+
" and I am "+std::to_string(person.getAge());
return"Hello, my name is "+person.firstName+" "+person.lastName+" and I am "+std::to_string(person.age);
The version without getters performs this task 30% quicker than the version with getters. But why? It is because of the return by value of the getters functions. Returning by value makes a copy that results in poorer performance. Let’s compare the performance between person.getFirstName(); and person.firstName.
As you can see, accessing directly the first name instead of a getter is equivalent to a noop.
Getter by const reference
However, it is possible to not return by value but return by reference instead. Going that way, we will have the same performance as without getters. The new code will look like that
Here is the new solution that works everywhere. You need 2 getters. One for lvalue and one for rvalue (both xvalue and prvalue).
There is not a lot to say in this section. If you want to achieve the best performances, you must write a setter that takes a lvalue and one that takes a rvalue. However, it is generally fine to just have a setter that takes a value that will be moved. Nevertheless, you have to pay the price of an extra move. However, that way, you cannot just make a little enhancement. You must replace the whole variable. If you just wanted to replace one A by a D in a name, it will not be possible by using setters. However, using direct access makes it possible.
What about immutable variables?
Ones will tell you to just make the member attribute as const. However, I am not ok with this solution. Indeed, making it const will prevent the move semantic and will lead to unnecessary copy.
I have no magic solution to propose you right now. Nevertheless, we can write a wrapper which we can named immutable<T>. This wrapper must be able to be :
Since it is immutable, it must not be assignable
It can be copy constructible or move constructible
It must be convertible to const T& when being a lvalue
It must be convertible to T when being a rvalue
It must be used like other wrapper through operator* or operator->.
It must be easy to get the address of the underlying object.
I would not say that getters and setters are bad. However, when you do not need to do anything else in your getter and your setter, achieving the best performance, safety and flexibility lead to writing:
3 getters (or even 4): const lvalue, rvalue, const rvalue, and if you want, non-const lvalue (even if sounds really weird since it is easier to just use direct access)
1 setter (or 2 if you want to have the maximum performance)
It is a lot of boilerplate for almost anything.
Some people will tell you that getters and setters enforce encapsulation, but they don’t. Encapsulation is not just making attributes private. It is about hiding things from users, and for structure-like objects, you rarely want to hide anything.
My advice is: when you have a structure like objects, just don’t use getter and setters and go with public / direct access. To be simple, if your setter does not enforce any invariant, you don’t need a private attribute.
PS: For people who use libraries using shallow copy, the performance impact is less important. However, you still need to write 2 functions instead of 0. Don’t forget, the less code you write, the less bugged it will be, easier to maintain, and easier to read.
And you ? Do you use getters and setters? And why?