May 5, 2019

Learning C++, day two

My first steps:

  1. I have enrolled for a Pluralsight course
  2. I have installed a C++ syntax extension on my beloved editor.

Discovery #1: Classes feel like language extensions

In Go, primitive types are special. Some properties only apply to them; for example, there is no way of defining a behaviour for make(myType). Every primitive type brings its own built-in constructor, and custom types inherit the constructor from the primitive type they’re based on. If you want a new instance of your type to be any different than “the zero value for the underlying type”, then you have to declare a very explicit New function and write a compelling comment advocating its use.

In C++, by defining a constructor you replace the built-in constructor. Declaring a new object of that class will trigger its constructor. While this simple fact may seem obvious to many, it’s not necessarily obvious for a Go programmer like me 😁.

Discovery #2: Memory is deallocated automatically

Contrary to my expectations, it seems like I don’t have to explicitly free the memory when I’m done with a variable in C++. Instead, the deconstructor is called and the corresponding memory is freed automatically, as soon as the variable is out of scope. Sounds like a good idea!

However, I suspect that the future me, naïvely passing pointers around, will eventually get a very vivid idea why reference-counting garbage collection is so popular nowadays.

Discovery #3: no reflection?

For every class, I declare properties and methods (why do they call them “functions”?) upfront in a Person.h header file. Then I write the function bodies in Person.cpp. At first sight, it looks redundant. But there’s a bright side: by just reading the .h file, I know exactly what information the caller function knows about a specific class.

What is this Precompiled header voodoo?

I guess I will eventually find out. In the meantime:

rm src/*.ghc && make build

¯\(ツ)

My first C++ lines

// src/Person.h

#include <string>

class Person {
// Private properties are not reachable from outside the class itself.
// In other terms: only functions attached to this class can see them.
private:
	std::string firstname;
	std::string lastname;
	int arbitrarynumber;

public:
	// A function named after the class is the constructor.
	// This one takes no arguments.
	Person();

	// This is the destructor: a function that will be called as soon as
	// the object gets out of scope.
	~Person();

	// This is an alternative constructor: if the object is instantiated
	// with this specific function signature, then this constructor will be
	// run.
	Person(std::string first, std::string last, int arbitrary);

	// Apaprently, it is common to provide public getters instead of
	// exposing the object properties outside the class.
	//
	// In this case, we will join firstname and lastname.
	std::string getName();

};
// src/Person.cpp

#include <iostream>
#include "Person.h"

// If the Person is declared without arguments, then it's me by default.
//
// One interesting thing here, is that the properties are initialised **before**
// the actual function body. This way, we avoid them being first initialised by the
// compiler, and a second time by us.
Person::Person() :
	firstname("Pierre"),
	lastname("P"),
	arbitrarynumber(34)
{
	// Produce some output, to let us know when this constructor is called.
	std::cout << "constructing..." << getName() << std::endl;
}

// If two strings and an int are passed on declaration, then this constructor
// is called.
Person::Person(std::string first, std::string last, int arbitrary) :
	firstname(first),
	lastname(last),
	arbitrarynumber(arbitrary)
{
	std::cout << "constructing..." << getName() << std::endl;
}

// The destructor. Just to log stuff.
Person::~Person()
{
	std::cout << "destructing " << getName() << std::endl;
}

std::string Person::getName()
{
	return firstname + " " + lastname;
}
// src/Main.cpp

#include <iostream>
#include "Person.h"

int main()
{
	std::string pierreName;
	{
		// Here we create me and save my name in the variable declared
		// in the outer block.
		Person pierre;
		pierreName = pierre.getName();
	}

	Person vale("Valentina", "C", 32);
	std::string valeName = vale.getName();

	std::cout << valeName << std::endl;
	std::cout << pierreName << std::endl;

	// In my experiments, this value is returned to the shell as the exit
	// code.
	return 0;
}
# Output of ./a.out
constructing...Pierre P
destructing Pierre P
constructing...Valentina C
Valentina C
Pierre P
destructing Valentina C