Introduction to C++ for iOS Developers: Part 2

In part 2 of this introduction to C++ for iOS developers, you will learn about polymorphism, virtual functions, templates, the STL, and more. By Matt Galloway.

Leave a rating/review
Save for later
Share
You are currently viewing page 4 of 6 of this article. Click here to view the first page.

Template Classes

Templates don’t stop at functions, however. It’s also possible to use templates with entire classes!

Imagine you want a class that holds a triplet of values — that is, three values that you’re going to use to hold some data. At first you want to use it with integers, so you write it like this:

class IntTriplet {
  private:
    int a, b, c;

  public:
    IntTriplet(int a, int b, int c) : a(a), b(b), c(c) {}

    int getA() { return a; }
    int getB() { return b; }
    int getC() { return c; }
};

But then you continue developing your application and realise that you need a triplet that stores floats. This time you write another class, like this:

class FloatTriplet {
  private:
    float a, b, c;

  public:
    FloatTriplet(float a, float b, float c) : a(a), b(b), c(c) {}

    float getA() { return a; }
    float getB() { return b; }
    float getC() { return c; }
};

It would appear that templates would be helpful here too — and it turns out that they are! In the same way that functions can use templates, so can entire classes. The syntax is identical. The above two classes could be rewritten like this:

template <typename T>
class Triplet {
  private:
    T a, b, c;

  public:
    Triplet(T a, T b, T c) : a(a), b(b), c(c) {}

    T getA() { return a; }
    T getB() { return b; }
    T getC() { return c; }
};

Using such a template class, however, requires slightly altering how you use the class. Template functions work without changing code, because the types of the arguments permit the compiler to infer what’s going on. However you need to tell the compiler which type you want a template class to use.

Fortunately, it’s very straightforward. Using the template class above is as easy as this:

Triplet<int> intTriplet(1, 2, 3);
Triplet<float> floatTriplet(3.141, 2.901, 10.5);
Triplet<Person> personTriplet(Person(“Matt”), Person(“Ray”), Person(“Bob”));

Powerful, isn't it?

pic5

But wait! There’s more!

Template functions and classes are not limited to just a single unknown type. The Triplet class could be extended to support any three types, rather than each value having to be the same type.

To do this, it’s just a matter of extending the template definition to supply more types, like so:

template <typename TA, typename TB, typename TC>
class Triplet {
  private:
    TA a;
    TB b;
    TC c;

  public:
    Triplet(TA a, TB b, TC c) : a(a), b(b), c(c) {}

    TA getA() { return a; }
    TB getB() { return b; }
    TC getC() { return c; }
};

Three different types form the template above; each is then used in the appropriate place in the code.

Using such a template is also very easy, as shown below:

Triplet<int, float, Person> mixedTriplet(1, 3.141, Person(“Matt”));

So that's it for templates! Now let's take a look at a library that makes heavy use of this feature - the Standard Template Library.

Standard Template Library (STL)

Any self-respecting programming language has a standard library that contains commonly used data structures, algorithms and functions. In Objective-C you have Foundation. This contains NSArray, NSDictionary among other familiar and not-so-familiar members. In C++, it is the Standard Template Library — or STL, for short — that contains this standard code.

The reason it is called the Standard Template Library is that it makes extensive use of templates. Funny that, eh? :]

There are many things that are useful in the STL; covering all of them would take far too long, so I’m just going to cover the most important bits here.

Containers

Arrays, dictionaries, and sets: all of these are containers of other objects. In Objective-C, the Foundation framework contains implementations of the most commonly used containers. In C++, the STL contains these implementations. In fact, the STL contains quite a few more container classes than Foundation does.

In the STL there are a couple of different equivalents to NSArray. The first is called vector and the second is called list. Both of these can represent a sequence of objects, but each has its own benefits and downsides. Once again, it’s a matter of choosing from the range of options given to you by C++.

First of all, let’s look at vector, which is used like this:

#include <vector>

std::vector<int> v;
v.push_back(1);
v.push_back(2);
v.push_back(3);
v.push_back(4);
v.push_back(5);

Notice the use of std::; this is because most of the STL lies within the std namespace. The STL puts all of its classes inside its own namespace called "std" to avoid potential naming conflicts with other code.

In the above code, you first create a vector to store ints, then five ints are pushed onto the end of the vector. At the end of this operation, the vector will contain 1, 2, 3, 4 and 5 — in that order.

One thing to note here is that all containers are mutable; there are no mutable and immutable variants, like there are in Objective-C.

Accessing elements of a vector is done like so:

int first = v[1];
int outOfBounds = v.at(100);

Both of these are valid ways to access elements of a vector. The first notation uses the square brackets; this is how you index into a C-style array. It’s also how you are now able to index into an NSArray since subscripting was added to Objective-C.

The second line above uses the at() member function. This works the same as the square brackets, except it also checks if the index is within the bounds of the vector. If it’s not, then it throws an exception.

A vector is implemented as a single, contiguous, block of memory. Its size is therefore the size of the object being stored (so four or eight bytes for integers, depending on if you have 32-bit or 64-bit architecture in use) multiplied by the number of objects in the vector.

Adding elements to a vector is expensive, because a new block of memory needs to be allocated for the new size vector. However accessing a certain index is fast, because it’s a matter of simply reading the right number of bytes into the memory store.

std::list is quite similar to std::vector; however, a list is implemented slightly differently. Instead of being a contiguous block of memory, it is implemented as a doubly linked list. This means that each element of the list contains the data at that element, along with a pointer to the next and previous elements.

Because it is a doubly linked list, insertion and deletion operations are trivial. However accessing the n’th element in the list requires walking from the 0’th to the n’th.

That said, using a list is very similar to vectors:

#include <list>

std::list<int> l;
l.push_back(1);
l.push_back(2);
l.push_back(3);
l.push_back(4);
l.push_back(5);

Just like the vector example above, this creates a sequence 1, 2, 3, 4 and 5 in order. This time, however, you cannot use the square brackets or at() member function to access a certain element in the list. Instead you must use a concept known as iterators to walk through the list.

Here’s how you would loop through each item of a list:

std::list<int>::iterator i;
for (i = l.begin(); i != l.end(); i++) {
    int thisInt = *i;
    // Do something with thisInt
}

Most container classes have the concept of an iterator. The idea is that an iterator is an object that can move back and forth through the collection and points to a specific member. You increment the iterator to move it forward, or decrement it to move is back.

Obtaining the value at the current location of the iterator is as simple as using the dereference operator (*).

Note: In the above code, there are a couple of instances of operator overloading to be seen. The i++ is the iterator overloading the increment operator ++. *i overloads the dereference operator *. The STL makes heavy use of operator overloading like this.

In addition to vector and list, there are many other containers in C++. They all have different features. Just as in Objective-C there is a set, std::set, and a dictionary, std::map. Another commonly used container is std::pair which stores just two values.

Contributors

Over 300 content creators. Join our team.