(C++) Answer of exercise \#3: Don't give away your internals, \#0
August 30, 2019 · View on GitHub
This is part #0 of the answer of exercise #3: Don't give away your internals.
Part #0: checking your member function design
Let's start by defining a test suite. Note that the definitions are not needed, the compiler will give the warnings and errors needed already.
#include <vector> struct Animal { int mX; //It does not matter what mX is... }; struct Zoo { //The GetAnimals method to be private: std::vector<Animal> mAnimals; }; int main() { Zoo zoo; zoo.GetAnimals[0].mX = 123; //Must not compile! }
For the GetAnimals member function, there are many possibilities:
- The member function can be a const member function yes or no
- The return type can be a const return type yes or no
- The return type can also be std::vector<Animal> or std::vector<const Animals>
- The animals can be given by using a reference or a copy
These sixteen possible member functions are:
std::vector<Animal> GetAnimals() ; const std::vector<Animal> GetAnimals() ; std::vector<const Animal> GetAnimals() ; const std::vector<const Animal> GetAnimals() ; std::vector<Animal>& GetAnimals() ; const std::vector<Animal>& GetAnimals() ; std::vector<const Animal>& GetAnimals() ; const std::vector<const Animal>& GetAnimals() ; std::vector<Animal> GetAnimals() const; const std::vector<Animal> GetAnimals() const; std::vector<const Animal> GetAnimals() const; const std::vector<const Animal> GetAnimals() const; std::vector<Animal>& GetAnimals() const; const std::vector<Animal>& GetAnimals() const; std::vector<const Animal>& GetAnimals() const; const std::vector<const Animal>& GetAnimals() const;
The first halve to be taken off the list of possiblities are those that return a copy of the std::vector of Animals. We don't need a copy of all those animals (imagine that a Zoo has millions of animals!). We do need to do is get a safe read-only reference to the animals (Note: perhaps later we will get back to this!).
The second halve to be taken off the list of possiblities are the non-const-methods. We intend to only read from the Animals, so it should not change our Zoo. It should also be possible to read the Animals from a const Zoo.
This leaves only four options left, which I'll give numbers from now on:
std::vector< Animal>& GetAnimals0() const; const std::vector< Animal>& GetAnimals1() const; std::vector<const Animal>& GetAnimals2() const; const std::vector<const Animal>& GetAnimals3() const;
Now the real tests start, even before defining the GetAnimal member functions.
The following line must not compile:
zoo.GetAnimalsX()[0].mX = 123;
Three out of four correctly refrain from compiling, where GetAnimals0 just performs the unwanted modification. GetAnimals0 is marked as a potential source for bugs, and taken off our list.
With three member functions left in the race, it's time to define them:
const std::vector< Animal>& GetAnimals1() const { return mAnimals; } std::vector<const Animal>& GetAnimals2() const { return mAnimals; } //Does not compile const std::vector<const Animal>& GetAnimals3() const { return mAnimals; } //Does not compile
It appears that GetAnimals2 and GetAnimals3 do not compile. This is correct: we indeed give away a reference to a std::vector<Animal> instead of to a std::vector<const Animal>. If we want to return a std::vector<const Animal> then we need to make a copy of all those animals. Let's refrain from this (for now) and call GetAnimals1 the winner.
struct Zoo { const std::vector<Animal>& GetAnimals() const { return mAnimals; } //Brilliant! private: std::vector<Animal> mAnimals; };
Go to the answer of the follow-up question: Answer of exercise #3: Don't give away your internals, #1
Epilogue
The exercise is called 'Don't give away your internals' (after [1]). One of the points of this item was the question: 'What do you want to do with this information?'. If you want to use std::cout on the implementation of Zoo (that is, the std::vector of Animal), why not enable to use std::cout on Zoo itself? Think if you perhaps do not need the GetAnimals (or similar, as in your code) method after all.
References
- Herb Sutter, Andrei Alexandrescu. C++ coding standards: 101 rules, guidelines, and best practices. ISBN: 0-32-111358-6. Item 28: 'Don't give away your internals'