Introduction to C++ for iOS Developers: Part 1
In part 1 of this introduction to C++ for iOS developers, you will learn about C++ syntax, inheritance, namespaces, memory management, and more. By Matt Galloway.
Sign up/Sign in
With a free Kodeco account you can download source code, track your progress, bookmark, personalise your learner profile and more!
Create accountAlready a member of Kodeco? Sign in
Sign up/Sign in
With a free Kodeco account you can download source code, track your progress, bookmark, personalise your learner profile and more!
Create accountAlready a member of Kodeco? Sign in
Contents
Introduction to C++ for iOS Developers: Part 1
30 mins
C++ new and delete
C++ introduces a couple of keywords to help with memory management of heap objects; they’re used to create and destroy objects on the heap.
Creating an object is done like this:
Person *person = new Person();
When you are finished with the object, you can get rid of it like this:
delete person;
In fact, this even works for scalar types in C++:
int *x = new int();
*x = 5;
delete x;
You can think of these operations as equivalent to the initialization and destruction of objects in Objective-C. Initialization in C++, using new Person()
, is equivalent to [[Person alloc] init]
in Objective-C.
There is no specific equivalent to delete
in Objective-C, though. As I’m sure you’re aware, deallocation of an Objective-C object is handled for you by the runtime when its retain count drops to zero. Remember, C++ doesn’t do reference counting for you. You’re in charge of deleting the object when you’ve finished with it.
You now have an overview of memory management in C++; the takeaway here is that it’s a lot more complex to manage memory in C++ than in Objective-C. You really need to think about what’s going on and keep track of all of your objects.
Accessing Members of Stack and Heap Objects
You’ve seen that objects can be created on either the stack or the heap in C++. However, there’s one subtle but important difference when using each type: the way you access member variables and member functions is slightly different.
When using stack objects, you need to use the dot operator (.). With heap objects, you need to dereference the pointer using the arrow operator (->), like so:
Person stackPerson;
stackPerson.name = “Bob Smith”; ///< Setting a member variable
stackPerson.doSomething(); ///< Calling a member function
Person *heapPerson = new Person();
heapPerson->name = “Bob Smith”; ///< Setting a member variable
heapPerson->doSomething(); ///< Calling a member function
The difference is subtle, but important to note.
You'll also see the arrow operator used with the this
pointer; it's the same thing as the self
pointer in Objective-C, and is used inside class member functions to access the current object.
The following C++ example shows the usage of the arrow operator:
Person::doSomething() {
this->doSomethingElse();
}
This leads into a common gotcha with C++. In Objective-C, if you call a method on a nil pointer, your app will still run fine:
myPerson = nil;
[myPerson doSomething]; // does nothing
However, in C++ if you try to call a method or access an instance variable on a NULL pointer, your app will crash:
myPerson = NULL;
myPerson->doSomething(); // crash!
Therefore, you must be very careful in C++ to make sure you do not attempt to do work on NULL pointers.
References
When you pass an object to a function, you're passing a copy of the object, not the obejct itself. For example, consider the following block of C++ code:
void changeValue(int x) {
x = 5;
}
// …
int x = 1;
changeValue(x);
// x still equals 1
This is pretty straightforward and comes as no surprise. But look what happens when you do the same thing with a function that takes an object as a parameter:
class Foo {
public:
int x;
};
void changeValue(Foo foo) {
foo.x = 5;
}
// …
Foo foo;
foo.x = 1;
changeValue(foo);
// foo.x still equals 1
Maybe that’s a little more surprising to you. If you think about it, it’s no different from the simple int
case. What’s happening is a copy of the Foo
object is made before passing it to the function.
Sometimes, though, you do want to pass the actual object. One way to do it would be to change the function to take a pointer to the object, rather than the object itself. But then that adds extra code whenever you call the function.
C++ adds a new concept which allows you to pass a variable “by reference”. This means that no copy is made; this is in contrast to the above examples which demonstrates passing “by value”.
It’s simple to change your calls to pass by reference; you simply use the address operator by adding an ampersand (&) in front of the variable in the function signature, like so:
void changeValue(Foo &foo) {
foo.x = 5;
}
// …
Foo foo;
foo.x = 1;
changeValue(foo);
// foo.x equals 5
It also works for non-class variables:
void changeValue(int &x) {
x = 5;
}
// …
int x = 1;
changeValue(x);
// x equals 5
Pass by reference can be very useful and can significantly improve performance. This is particularly true if making a copy of an object is extremely expensive, such as working with a huge list where creating a copy means performing a deep copy on the object.
Inheritance
An object-oriented language just wouldn’t be complete without inheritance, and C++ certainly doesn’t buck this trend. Consider the following two Objective-C classes where one inherits from the other:
@interface Person : NSObject
@end
@interface Employee : Person
@end
The same thing can be expressed in C++, in a very similar way:
class Person {
};
class Employee : public Person {
};
The only difference in C++ is the addition of the public
keyword. Here, Employee
inherits publicly from Person
. This means that all the public members of Person
remain public in Employee
.
If you replaced public
with private
, then the public members of Person
would become private in Employee
. For more information about this topic, I suggest reading through a great article about inheritance and access specifiers here.
That’s the easy part about inheritance — now comes the hard bit. C++ is different from Objective-C in that it allows multiple inheritance. This lets a class inherit from two or more base classes. That might seem alien to you, especially if you haven't used a language other than Objective-C.
Here is an example of multiple inheritance in C++:
class Player {
void play();
};
class Manager {
void manage();
};
class PlayerManager : public Player, public Manager {
};
In this example, there are two base classes and one class which inherits from both. This means that the PlayerManager
has access to all of the member variables and functions of each base class. Neat, eh? As I’m sure you’re painfully aware, there is no way to do this in Objective-C.
Well...that’s not strictly true, is it?
The astute reader will notice that there is something similar in Objective-C: protocols. While not quite the same thing as multiple inheritance, both techniques aim to solve the same problem: providing a mechanism to link together classes that serve similar purposes.
Protocols are slightly different in that a protocol has no implementation; rather, it simply serves as a way to describe an interface to which a class must conform.
In Objective-C, the example above could be written as follows:
@protocol Player
- (void)play;
@end
@protocol Manager
- (void)manage;
@end
@interface Player : NSObject <Player>
@end
@interface Manager : NSObject <Manager>
@end
@interface PlayerManager : NSObject <Player, Manager>
@end
Of course, this is ever so slightly contrived, but you get the picture. In Objective-C you would have to implement both play
and manage
in the PlayerManager
class, whereas in C++ you would just implement the method in each base class and then the PlayerManager
class would automatically inherit each implementation.
In practice, though, multiple inheritance can sometimes lead to confusion and complication. It is often regarded among C++ developers as a dangerous tool to be avoided at all costs unless absolutely necessary.
Why? Consider what could happen if both base classes implemented a function with the same name and that accepted the same arguments — that is, both would have the same prototype. In this case, you'd need a way to disambiguate one from the other. For example, imagine both Player
and Manager
classes had a function named foo
.
You would need to disambiguate like so:
PlayerManager p;
p.foo(); ///< Error! Which foo?
p.Player::foo(); ///< Call foo from Player
p.Manager::foo(); ///< Call foo from Manager
This is certainly doable, but it adds confusion and a layer of complexity which is best avoided. The decision is up to the user of PlayerManager
. Using protocols leaves it up to the PlayerManager
class to implement foo
so that there's only one implementation — and zero confusion.