C++ is an object-orientated language, and so heavily makes use of classes and objects. Objects are to classes as variables are to types; a class describes how something is structured and what it’s used for, and an object is some instance of that class. For example, we may want to use arithmetic that is more precise than what float or double (or long double!) can provide. Ideally we would use rational numbers (i.e. fractions), but C++ doesn’t have a rational number type so instead we must implement our own using a class.

A class definition begins with the class keyword followed by the class name, similar to a variable definition (though class is not a type). Everything in the following {} is a part of the class definition, which can include access specifiers as on lines 4 and 13, member variable and function declarations as on lines 8, 9, 11, and 13, and constructors as on lines 16 and 28. As you can see a constructor declaration looks a lot like a function declaration (and likewise for constructor definitions, which we’ve also used), and indeed they’re very similar except that constructors must have the same name as the class, do not have a return type (not even void), and are only called when an object of a class is created, as follows.

Here we’ve created two objects foo and bar which are instances of the class Rational. We have to call a constructor when we declare objects (which are still a kind of variable), and which constructor to call is determined by the number and type of the arguments just as in normal function overloading. Note that attempting to declare the object wrong without using a constructor or by calling a constructor that doesn’t exist will result in a compilation error.

When foo is created the Rational(signed long long x, unsigned long long y) constructor is called with arguments 3 and 6. The constructor then sets the member variables to these arguments. Member variables can be thought of as only being in the scope of the object they belong to, so whilst both foo and bar know about their own a and b, they are completely separate from each other and belong to their respective objects. This wouldn’t make any sense for example.

The same applies for member functions; trying to call the functions reduce or toi will result in an error, because they don’t exist outside of foo or bar. However, this is where access specifiers come in, as sometimes member functions or variables can be accessed from outside the object! Access specifiers define the member access of each member variable and function (and constructors too), which determines whether or not a member can be used outside of the object it belongs to. The private specifier removes all access from outside of the object, and the public specifier grants complete access. We’ve given a and b private to hide the internal implementation of the Rational, and also to stop the situation where the object may expect a/b to be in lowest terms, but it isn’t because its been incorrectly change from outside the class.

Note that the members are still specific to the object they belong to, it’s just that they can be used in functions which are not members of its class. To reflect this, members are accessed using a . after the object name.

Sometimes we want to restrict modification of member variables but still want them to be accessible. In this case we define member getter functions which return the value of a member variable. This way we can access the variable but cannot modify it from outside of the class. There’s no special syntax for these, they’re just normal public functions with no arguments.

### Destructors

It’s worth noting that there is a counterpart to the constructor, the destructor, which is called whenever an object goes out of its scope. Since they’re called automatically by the compiler they can’t take any arguments, but must take the name of the class as a constructor does; the difference is marked by a ~ in front of the name.

The output of this program is

If you don’t define a destructor then the compiler will create one automatically that does nothing. Right now they aren’t particularly useful because most objects won’t need to do anything when they go out of scope, but they’ll become necessary later when we deal with dynamic memory and there will be things for them to do.

### Manipulating Objects

An object is still a variable, and so if you can do something with a typed variable you can probably do it with an object to. For example, as well as defining objects using constructors you can also use the assignment operator =, you can create arrays of objects, you can write functions that return objects or take objects as arguments, and you can create references to objects.

It’s also possible to call the constructor of a class directly to create an r-value that can be assigned to a new variable using =.

Of course the first is much shorter, but it requires the variable to have a name, which is not the case when creating arrays. Instead the second form is used.

### Defining Functions Outside of a Class

Member functions are still functions are heart, and so can be declared and defined in separate locations; so long as the declaration occurs before any usage of the member function, you’re fine. The declaration is required to be in the class, but the definition is not, and can even be placed in a separate file. Common convention is to place the class definition in a header file with the same name as the class and place all the constructor and function definitions in a similarly named source file.

When definining functions (and constructors) outside of classes like this you have to specify which class the function belongs to (two classes might have a function called reduce) by placing the class name and a :: before the name of the function. Note that you don’t use a . like when accessing a member function! (This is to make the syntax consistent with namespaces, one of which—std—you’ve already seen.)

Whilst both foo and bar represent rational numbers, foo * bar will cause a compilation error, because C++ doesn’t know what * means when dealing with objects and not numeric types. We could get around this by creating a multiply function which returned a new Rational equal to the product of the existing ones, but it wouldn’t make sense for it to be a member function because we aren’t modifying anything (and foo.multiply(foo, bar) is pretty cumbersome) and sowe would have to come up with a unique name such as rational_multiply(foo, bar) so as to not conflict with any other multiply functions that might be defined somewhere. (This would be the C approach.)

Luckily though, we can just define * to mean exactly what we want! (Note that this is still not a member function.)

Every operator we have seen so far can be overloaded, but that doesn’t always make it a good idea! If you decide to overload operators then their overloaded meaning should be consistent with the original (don’t overload + to mean division for example), and if you overload an operator you should overload its corresponding compound assigment operator (e.g. * and *=) too. A common idiom is to overload the compound operator first and specify the standard operator in terms of it, but that requires some syntax we haven’t quite covered yet. (Pointers, to be specific.)

### Exercise

Build upon the vector functions from the last exercise by creating a Vec3 class which represents a vector with 3 float components. You should add a mag member function to calculate the length of the vector, and overload the + and - operators to handle vector addition and subtraction. Note these operations are defined component-wise, so $(a, b, c) + (x, y, z) = (a + x, b + y, c + z)$. Additionally you should define the * operator for scalar multiplication, defined by $k * (a, b, c) = (k*a, k*b, k*c)$.

It’s up to you what to make public and what to make private, but the following code should work with your class.

Solution