Single responsibility principle A class should have only a single responsibility (i.e. changes to only one part of the software’s specification should be able to affect the specification of the class). A class should have only one reason to change. Separate GUI and the business logic. Low coupling, high cohesion. Open/closed principle Software entities should be open for extension, but closed for modification. However, realistically you don’t know which bits need to be extensible. [Read More]

Object-oriented programming

OOP I did it again

OOP is a programming paradigm based on the concept of “objects”, which may contain data, in the form of fields often known as attributes; and code in the form of procedures, often known as methods. We are aiming to group related data and functions. Encapsulation: tie the data and the functions that are acting together in a class. Composition Delegation is calling a routine “in another context”, for instance calling a base class member function from an instance of a derived class. [Read More]

How does your vector grow?

How many times is the destructor for the “fax” class called below after populating a five element STL vector? std::vector<fax> y; for (size_t i = 0; i < 5; ++i) y.push_back(fax()); Remarkably it’s 17 times! Run the code on compiler explorer. For each push we call the default constructor to create a transient object and then call the copy constructor to populate the new element with its data. So far so good. [Read More]

Bird in a bird

How many times is the destructor called?

struct bird { ~bird() { std::puts("in a bird"); } }; bird in = bird(bird(bird(bird(bird())))); The destructor is called once! Thanks to C++'s return value optimisation. You can disable this behaviour by passing -fno-elide-constructors to the compiler. However, if you’re using a standard later than C++14 then it happens regardless. So does this mean we can’t let copy constructors cause side-effects, lest the compiler doesn’t bother calling them? See copy elision and run the example in Compiler Explorer. [Read More]

Modern C++ migration

Welcome to the std::promised land

Auto Type inference is a game changer. Essentially you can simplify complicated (or unknown) type declarations with auto. But it can be a balance of convenience over readability. int x1 = 5; // Explicit auto x2 = 5; // What's the underlying type? std::vector<std::string> moon = {"Don't", "look", "at", "the", "finger"}; auto finger = moon.front(); And there are a few perfectly valid gotchas. Let’s create a variable and a reference to it, updating y2 (below) also updates y1 as expected. [Read More]