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 2 of 6 of this article. Click here to view the first page.

Constructors and Destructors

Every object goes through two very important stages in its life: construction and destruction. C++ allows you to control both of these stages. The equivalent of these stages in Objective-C is the initializer method (i.e. init or any other method starting with init) and dealloc.

Constructors in C++ are defined by functions that share the same name as the class. You can have any number of them that you like, just as you can have any number of initializer methods in Objective-C.

For example, here is a class that has a couple of different constructors:

class Foo {
  private:
    int x;

  public:
    Foo() {
        x = 0;
    }

    Foo(int x) {
        this->x = x;
    }
};

Here there are two constructors. One is considered the default constructor: Foo(). The other takes a parameter to set the member variable.

If all you’re doing in the constructor is to set internal state, as in the above example, there’s a way to do that with less code. Instead of implementing the setting of the member variables yourself, you can use the following syntax:

class Foo {
  private:
    int x;

  public:
    Foo() : x(0) {
    }

    Foo(int x) : x(x) {
    }
};

Usually if you’re just setting member variables you would use this syntax. If, however, you need to perform some logic or call other functions, then you would implement the function body. You can also use a combination of the two.

When using inheritance, you need a way to call up to the super-class’s constructor. In Objective-C you do this by always calling the super-class’s designated initializer first.

In C++, you would do it as follows:

class Foo {
  private:
    int x;

  public:
    Foo() : x(0) {
    }

    Foo(int x) : x(x) {
    }
};

class Bar : public Foo {
  private:
    int y;

  public:
    Bar() : Foo(), y(0) {
    }

    Bar(int x) : Foo(x), y(0) {
    }

    Bar(int x, int y) : Foo(x), y(y) {
    }
};

The call to the super-class’s constructor is indicated by the first element in the list after the function signature. You can call through to any super-class constructor you want.

C++ code doesn’t tend to have a single designated initializer. Until recently, there was no way to call through to a constructor of the same class. In Objective-C, it’s common to have one designated initializer that every other initialiser calls, and only the designated initializer calls through to the super-class’s designated initializer. For example:

@interface Foo : NSObject
@end

@implementation Foo

- (id)init {
    if (self = [super init]) { ///< Call to super’s designated initialiser
    }
    return self;
}

- (id)initWithFoo:(id)foo {
    if (self = [self init]) { ///< Call to self’s designated initialiser
        // …
    }
    return self;
}

- (id)initWithBar:(id)bar {
    if (self = [self init]) { ///< Call to self’s designated initialiser
        // …
    }
    return self;
}

@end

In C++, while you can call through to a super-class’s constructor, until recently it was illegal to call through to one of your own constructors. Therefore the following workaround was quite common:

class Bar : public Foo {
  private:
    int y;
    void commonInit() {
        // Perform common initialisation
    }

  public:
    Bar() : Foo() {
        this->commonInit();
    }

    Bar(int y) : Foo(), y(y) {
        this->commonInit();
    }
};

However, this is rather cumbersome. Why can’t you just have Bar(int y) call through to Bar() and then have the contents of Bar::commonInit() in Bar()? That’s exactly how it works in Objective-C, after all.

In 2011, the latest C++ standard became a reality: C++11. In this updated standard it is indeed possible to do just this. There is still a lot of C++ code out there that hasn't been updated for the C++11 standard, which is why it's important to know both approaches. Any post-2011 C++ code will more likely do the following:

class Bar : public Foo {
  private:
    int y;

  public:
    Bar() : Foo() {
        // Perform common initialisation
    }

    Bar(int y) : Bar() {
        this->y = y;
    }
};

The only slight snag with this approach is that you can’t set member variables at the same time as calling a constructor from the same class. In the case above, the y member variable has to be set as part of the constructor body.

Note: C++11 became a full standard in 2011. It was originally called C++0x while being developed. This is because it was meant to mature sometime between 2000 and 2009, where the ‘x’ would be replaced by the last digit of the year. However it went on longer than expected and therefore ended up being called C++11! All modern compilers, including clang, now fully support C++11.

That covers construction, but what about destruction? That happens when an object is either deleted, if it’s a heap object, or goes out of scope, if it’s a stack object. In this function you're required to perform any necessary cleanup of the object.

A destructor cannot take any arguments, because that wouldn’t make much sense if you think about it for a moment. dealloc cannot take any arguments in Objective-C for the same reason. Therefore there can only be one destructor for each class.

The name of the destructor is the class name prefixed by a tilde (~). Here’s an example destructor:

class Foo {
  public:
    ~Foo() {
        printf(“Foo destructor\n”);
    }
};

Take a look at what happens when you have a class hierarchy:

class Bar : public Foo {
  public:
    ~Bar() {
        printf(“Bar destructor\n”);
    }
};

If you were to leave the code like this, then something strange would happen if you deleted an instance of Bar via a Foo pointer, like so:

Bar *b = new Bar();
Foo *f = (Foo*)b;
delete f;
// Output:
// Foo destructor

Err, that doesn’t seem right, does it? It’s Bar that’s been deleted, so why has Foo’s destructor been called?

Recall that this same problem happened earlier and you used virtual functions to get around it. This is the exact same problem. The compiler sees that it’s a Foo that needs deleting, and since Foo’s destructor is not marked as virtual, it thinks that is the function to call.

The way to fix this is to mark the destructor as virtual, like so:

class Foo {
  public:
    virtual ~Foo() {
        printf(“Foo destructor\n”);
    }
};

class Bar : public Foo {
  public:
    virtual ~Bar() {
        printf(“Bar destructor\n”);
    }
};

Bar *b = new Bar();
Foo *f = (Foo*)b;
delete f;
// Output:
// Bar destructor
// Foo destructor

That’s closer to the desired outcome — but the end result is different than what happened when virtual was used previously. This time, both functions are called. First the destructor for Bar is called, then the destructor for Foo is called. What gives?

This is because destructors are special cases. The Bar destructor automatically calls the Foo destructor because that’s the super-class’s destructor.

This is exactly what's required; just as in the pre-ARC days of Objective-C where you would call super’s dealloc.

I bet you’re thinking this:

pic4

You’d think that the compiler would just do this for you. Well, it could, but that wouldn’t be optimal in all cases.

For example, what if you never inherited from a certain class? If that destructor was virtual then there would be an indirection via the v-table every time an instance is deleted, and maybe you don’t want this indirection. C++ gives you the ability to make the decision yourself — yet another example of C++ being very powerful — but the developer really needs to keep on top of what’s going on.

Let this be a warning to you. Always make destructors virtual, unless you’re absolutely sure that you won’t inherit from that class!

Contributors

Over 300 content creators. Join our team.