Any (non-reference) variable used in a C++ program is stored somewhere in the computer’s memory when the program is being run, at some numerical memory address. Since this location is unique (we can’t have two different variables in the same location) we can refer to a variable by both its name, and its address. Because addresses are integer values, unlike variable names, we can manipulate them in powerful ways.

To find the address of a variable we use the & operator placed in front of the variable name. This gives us a new value with the pointer type, or specifically the pointer to X type, where X is whatever the type of the variable we are taking the address of; taking the address of an int gives an int pointer, taking the address of a Foo gives a Foo pointer, and so on. To denote this new type, we place a * after the type name, such as int* and Foo*.

A handy feature of pointers is that because they refer to an address, they can be used in a manner similar to references to create aliases for existing variables, which can then be passed to functions. For example, we can write an add function that increases the value of a variable by passing to it that variable’s address in the form of a pointer. To modify the value of the variable pointed to by the pointer (that is, the value at the address stored in the pointer) we have to dereference the pointer using the * operator placed in front of the pointer variable. If we were to modify the pointer’s value then we would be changing which address it stored, not which value is at the address.

### Arrays and Pointers

This shouldn’t be particularly exciting to you though, since we did all this with references a while ago and those didn’t require a bunch of & and * operators scattered all over the place. However, unlike references—which might not actually exist in memory (so you can’t take pointers to them!)—pointers hold actual numerical values and so can be manipulated as such. This isn’t useful in and of itself, but by knowing that the values in an array occupy a contiguous block of memory—where each value is directly after the next—we can use pointers in conjunction with arrays.

In the pointer example we create a new pointer that points to the first position in the array, then if the value of the pointer is not 9 we increment it so that it points to the next address and loop. Since arrays are contiguous blocks of memory the next address will be the address of the next position in the array. We then decrement the pointer to move it to the address of the preceeding element and then dereference it to find its value.

One possible flaw of the pointer method is that whilst it is readily apparent how to obtain the fifth value in the array using indices (array[4]) it’s much more labourious if you have to create a new pointer pointer to the first array position and then increment it 4 times. Pointers don’t just support incrementing though, they support arbitrary addition and subtraction and so the following are equivalent.

In fact, this is how the compiler internally represents arrays, by using pointers and pointer arithmetic! The [] operator is just a convenient shorthand for finding the values of elements in an array. What’s more, because arrays are implemented as pointers, if you don’t apply the [] operator to an array you obtain a pointer to its first element.

This doesn’t mean that the two are entirely interchangeable, when declaring arrays you still have to use [] instead of *.

### Dynamic Memory

So far we have known at compile time exactly how many variables we will need to use and we can declare them appropriately. This means that if we write a program that reads a selection of data from the user, we would have to constrain the maximum amount of data they can input; arrays only have a fixed number of elements. Using pointers we can remove this limitation by dynamically allocating memory, giving the program access to a different amount of memory to what it had at compile-time. By replacing our arrays with dynamically allocated memory we can handle an arbitrary amount of data; every time the user inputs some data we can just ask the computer to give our program some more space to store the value in. We can do this via the new[] and delete[] operators.

The new[] operator requires a type (or class) and the number of values of that type you want to allocate memory for. It then allocates the memory and evaluates to a pointer with value equal to the address of the first element in the new array. delete[] doesn’t require any parameters other than the pointer whose memory you want to deallocate (give back to the computer), but that pointer must be one which was already allocated using new[]. Once a pointer is deallocated or freed the data that it pointed to is lost, so don’t delete[] until you’re completely done. Make sure you do delete[] though, if you don’t deallocate a pointer then the memory will not be useable later in the program. (The operating system will automatically deallocate any allocated memory that you didn’t deallocate when the program ends, but don’t rely on this and make sure you always call delete[]!) For example, this code will break if left long enough.

Every loop iteration we’re reassigning the pointer to a different block of memory that the computer allocates, but because we aren’t deallocating the memory the computer doesn’t know that we aren’t using it anymore. The program will therefore use up more and more memory until your computer runs out of memory to allocate, in which case it will crash! Always make sure to deallocate your memory, and remember that even if you go out of scope of a pointer variable, the memory remains in use.

new[] and delete[] also have two counterparts—new and delete—which are used for allocating and deallocating memory for a single variable instead of an array. Their usage is the same but with the [] removed, and they are not compatible with each other; memory allocated with new must be deallocated with delete, and new[] with delete[].

### Pointers and Classes

If you tried accessing the members of vec_ptr in the last example you’ll have run into some problems, namely that the . operator no longer works. Instead, there is a separate pointer member access operator, ->. It works in exactly the same way as the ., but is used whenever the variable you are applying it to is a pointer to an object.

Note that the use of -> requires that the object is a pointer, not the member

Here we’ve made use of a destructor in order to deallocate the memory that was allocated during the life of any instances of A. Because ~A is called whenever the object goes out of scope, it ensures that the memory is deallocated and returned to the operating system. We’ve also checked that ptr actually has a value before deallocating it, as calling delete on nullptr or any value not obtained from a new will cause the program to error. (This method is not entirely robust, as it assumes that ptr was allocated using new and isn’t a pointer to an existing variable instead.)

All classes also implement a special pointer, this, which points to the object using it. So far we have evaded the problem of duplicating variable names in constructors by adding p to the constructor arguments (another common practice is adding m_ to the members), but the this pointer can also be used.

It is important to remember that dynamically allocated memory is not owned by any particular object, even if it was allocated by a member function. This means that you should be very careful when intialising objects which use dynamic memory. Consider for example

To combat this we need to use a copy constructor—which is called whenever copy initialisations such as Foo f2 = f1 occur—and will allocate extra memory for f2 instead of just copying the pointers. C++ provides default copy constructors as we’ve used here, but we can easily define our own when they aren’t suitable.

### Smart Pointers

This whole issue of remembering to deallocate pointers can be a bit tedious, but the fact that they aren’t deallocated when they leave the scope they were allocated in is rather handy; it allows us to allocate memory inside of a function but have it accessible outside of the function, something that doesn’t work when using variables. What would be useful is if memory stayed allocated until there were no longer any pointers pointing to it.

The standard library comes to the rescue with the introduction of smart pointers, or specifically the std::shared_ptr. std::shared_ptr is a class template defined in the memory header that shares the ownership of a piece of memory with other std::shared_ptrs. When the last std::shared_ptr sharing that memory is destroyed (goes out of scope) or is changed to point to another address then the memory will be deallocated, but it will remain allocated and useable until then.

Obviously this is a very useful feature, especially because std::shared_ptr objects behave like pointers and the * and -> operators (which have been overloaded) dereference the managed pointer. The class also has various member functions which can be used to see how many pointers are sharing the managed pointer for example.

There are also two other classes which manage pointers in similar ways; std::unique_ptr and std::weak_ptr. When std::unique_ptr manages a pointer that pointer cannot be managed by any other std::unique_ptr, and when it goes out of scope the allocated memory is deallocated. std::weak_ptr is different and is used to obtain temporary ownership of an std::shared_ptr when the existence of its managed pointer is not guaranteed. This is only really useful in threaded applications when we are not directly controlling pointer allocation, which we won’t cover, but I encourage you to read the documentation if you’re interested.

Instead of having to write code like

we can make use of the std::make_shared function

A similar function exists for std::unique_ptr, but only in the newer C++14 standard so your compiler may not support it yet. There is also a lot more to memory management in C++, but there’s far too much to cover here.

### Exercise

Modify the Vec3 class into a general Vec class that can contain an arbitrary number of elements determined when an object is instantiated. You should define a member function to compute the magnitude as before as well as at least scalar multiplication and vector addition operators; vector addition is only defined between two vectors when they both have the same number of elements, if they do not then it’s up to you to decide the operator’s behaviour. You should also overload the [] operator to access each component of the vector. There’s no need to define a sqrt function—you may use the one defined in the cmath header (don’t forget to link the cmath library if you do, usually using -lm)—and templates are not required (the solution will use double).

To handle a variable number of arguments in the constructor, either use C-style variadic arguments, C++ style std::initializer_lists, or use a pointer/array argument and a length argument as was used in previous tutorials. Solutions for all three methods will be provided!

The following should work with your code, with the appropriate constructor syntax selected.

Solution