TIL: C++ (part 2)

August 8, 2023

tags: #c++, #oop, #TIL


[Disclaimer: Much of what I’m saying here is likely to be completely wrong, partially wrong, or a major oversimplification. If you have any suggestions, please let me know.]

new in c++

The new operator in C++ is not for calling a constructor (like in JS), but allocates memory (like malloc or calloc in C).

So for example, this code in C++

int *ptr = new int

is equivalent to this code in C.

int *ptr = malloc(sizeof *ptr)

And this code in C++

int *arr = new int[10]()

is equivalent to this code in C.

int *ptr = calloc(10, sizeof *ptr)

When new is called to allocate an array of memory, it is automatically initialized to 0.

While it is possible to use malloc and calloc in C++, the general recommendation seems to be to use new whenever possible.

smart pointers

There are three types of smart pointers in C++ that help automatically free memory once the last reference goes out of scope.

The general consensus here and here seems to suggest favouring the use of smart pointers over raw pointers whenever possible.

class template

In C++ you can define a class template that uses a placeholder for a type (usually). When you call the class constructor, you also provide the template argument so the class knows which type to use for the placeholder.

You can also pass non-type arguments like integral values, pointers, references, and other templates.

A classic example is std::vector, which takes a template argument to know the underlying type of the array. For example, we write std::vector<int> or std::vector<std::string>, before creating the object.

A simple implementation of std::vector that uses a type as a template argument might look like this.

template <typename T>
class SimpleVector{
private:
    T* arr;
    std::size_t capacity;
    std::size_t size;
public:
    // constructor, methods, etc.
}

A simple implementation of a class template that uses an integral value as a template argument might look like this.

template <int Size>
class FixedArray {
    int data[Size];
public:
    FixedArray() {}
};

smart pointers are class templates

For example, unique_ptr is a class template, so it requires a template argument like std::unique_ptr<int>. Once the template has been instantiated into a class, you can then create an object.

There are only two ways of creating a unique_ptr. The old way was

std::unique_ptr<int> p(new int(10));

or using more modern syntax, we use make_unique

std::unique_ptr<int> p = std::make_unique<int>(10)

which is safer. You can read more about it here, but it mainly has to do with atomicity when allocating memory and assigning it to a variable.

The reason the next code example doesn’t compile is because we’re trying to assign a raw pointer to a unique_ptr.

// THIS CODE WON'T COMPILE.
std::unique_ptr<int> p = new int(10);

auto

C++ has a placeholder type called auto that basically says “deduce my type based on whatever is provided”. So using unique_ptr as an example, we could do this instead

auto p = std::make_unique<int>(10)

which is especially helpful if the type name is long. It also avoids duplication and is generally easier to understand. vErY cOoL.