Introduction to C++ for iOS Developers: Part 1

10 篇文章 0 订阅

Are you a master of Objective-C and looking for that next cool thing to learn? Try this article on for size; it introduces you to C++ for iOS developers.

As I’ll explain later, Objective-C works seamlessly with C and C++ code. Therefore, is very helpful for iOS developers to understand C++ for several reasons:

  • Sometimes you might want to use a library that was originally written in C++ in your app.
  • You might want to write part of your app’s code in C++, so it is more easily portable across platforms.
  • Having a good grounding of other languages helps you better understand programming in general.

This article is written for iOS developers who already understand Objective-C. It’s assumed that you already understand how to write Objective-C code and are familiar with basic C concepts such as types, pointers and functions.

Ready to learn some C++? Then dive right in!

Getting Started: A Brief History of Languages

C++ and Objective-C share some common ancestry: they both have their roots firmly planted in good old-fashioned C. This means that they are both “supersets” of the C language. Therefore, in both languages you can use C features alongside the additional features contained in each language.

If you’re familiar with Objective-C, you will likely have a rough understanding of the C++ code you encounter. For example, the scalar types such as int, float and char are present and behave in exactly the same way in both languages.

Both Objective-C and C++ add object-oriented features to C. If you’re unfamiliar with the term “object-oriented”, all you really need to understand is that it means data is represented by objects, which in turn are instances of classes. In fact, C++ was originally called “C with Classes”, which shows the underlying desire to make C++ object-oriented.

“So what about the differences, then?” I hear you ask. Well, the main difference is the approach to the object-oriented features. In C++, a lot of the action happens at compile time, whereas in Objective-C, much more happens at run time. You may already have dabbled with the Objective-C runtime to perform such trickery as method swizzling. In C++ this simply isn’t possible.

C++ also doesn’t have the plethora of introspection and reflection methods that Objective-C has. There’s no way to obtain the class of a C++ object as you would in Objective-C where you simply call the “class” method on an instance. Similarly there is no equivalent of isMemberOfClass or isKindOfClass in C++.

That’s a rough-and-ready introduction to C++ that shows the history and key differences between it and Obejctive-C. The history lesson is done — time to continue with some C++ language features! :]

C++ Classes

The first thing you need to know in any object-oriented language is how to define a class. In Objective-C, you create a header file and an implementation file to define a class. The exact same thing happens in C++; the syntax is also quite familar.

Here’s an example. The following is an Objective-C class:

// MyClass.h
 
#import <Foundation/Foundation.h>
 
@interface MyClass : NSObject
@end
 
// MyClass.m
 
#import “MyClass.h”
 
@implementation MyClass
@end

That should be clear to you as a seasoned iOS developer. But look at the equivalent in C++:

// MyClass.h
 
class MyClass {
};
 
// MyClass.cpp
 
#include “MyClass.h”
 
/* Nothing else in here */

There are a few distinct differences here. The first thing is that the implementation file in C++ doesn’t have anything in it. That’s because you’ve not declared any methods on the class. As well, an empty class doesn’t even need an @implemenation / @end block like Objective-C does.

In Objective-C, every class will almost always inherit from NSObject. You could create your own root class, which would mean that your class would have no superclass. But it’s unlikely you’ll ever do this unless you’re playing around with the runtime just for fun! This is in contrast to C++ where it’s quite common to create a class with no superclass as in the example above.

Another slight difference is #include versus #import. Objective-C adds the #import preprocessor directive to C. There is no equivalent in C++, so the standard, C-style, #include is used instead. Objective-C’s #import ensures that a file is only included once, but in C++ you must perform this check yourself.

Class Member Variables and Functions

Of course, there’s much more to a class than just declaring it. Just as in Objective-C, in C++ you can add instance variables and methods to a class. You might hear them termed differently in C++ though; they are more commonly called member variables and member functions.

Note: The term “method” is not really used in C++. The distinction is only used in Objective-C where a method is something called via a message dispatch. A function, on the other hand, is something called via a static C-style function call. I’ll explain more about static versus dynamic later in this article.

So how do you declare member variables and member functions then? Well, here’s an example:

class MyClass {
    int x;
    int y;
    float z;
 
    void foo();
    void bar();
};

Here there are three member variables and two member functions. But there’s a bit more to it than this in C++, as you can limit the scope of member variables and member functions in C++ and declare them as publicly or privately accessible. This can be used to limit what code can access each variable or function.

Consider the following example:

class MyClass {
  public:
    int x;
    int y;
    void foo();
 
  private:
    float z;
    void bar();
}

Here, x, y and foo are publicly accessible.This means they can be used outside of the MyClass class. However, z and bar are private. This means they can only be used from within MyClass itself. Member variables are private by default.

While this distinction does exist in Objective-C for instance variables, it’s rarely used. On the other hand, it is not possible in Objective-C to limit a method’s scope. Even if you only declare a method inside the implementation of a class, and don’t expose it in the interface, you technically could still call that method externally.

Methods in Objective-C are public or private only by convention. This is why a lot of developers choose to prefix a private method with something such as “p_” to document the distinction. This is in contrast to C++ where the compiler will throw an error if you try to call a private method from outside the class.

So how do you use a class? It’s very similar to Objective-C, really. You create an instance like so:

MyClass m;
m.x = 10;
m.y = 20;
m.foo();

Simple as that! This creates an instance of MyClass, sets x and y to 10 and 20 respectively and then calls foo on it.

Implementing Class Member Functions

You’ve seen how to define a class interface, but what about the functions? That’s quite simple, as it turns out. There’s a couple of ways you can do it.

The first way to implement a method is by defining it in the class’s implementation file — the .cpp file. You would do this as follows:

// MyClass.h
class MyClass {
    int x;
    int y;
    void foo();
};
 
// MyClass.cpp
#include “MyClass.h”
 
MyClass::foo() {
   // Do something
}

That’s the first way; it’s very similar to how you would do it in Objective-C. Notice the use of MyClass::; this is how you denote that the foo() function is being implemented as part of the MyClass class.

The second way to implement a method is something you can’t do in Objective-C. In C++ you can implement a method directly in the header file, like so:

// MyClass.h
class MyClass {
    int x;
    int y;
    void foo() {
        // Do something
    }
};

This might look quite odd to you if you’ve only ever used Objective-C. It is quite odd, but it can also be quite useful. When a function is declared in this way, the compiler can perform an optimisation called “inlining”. This means that when this function is called, instead of jumping to a new block of code, the entire function code is compiled inline at the call site.

While inlining can make code faster, it also increases the size of the compiled code because if the function is called more than once, the code will duplicated throughout the binary. If the function is quite large, or called a lot of times, then this can have a significant impact on the size of the binaries. This can cause a performance decrease because less code can fit in the cache, meaning there may be potentially more cache misses.

My goal here is to illustrate that C++ allows a lot of flexibility. It’s up to you as a developer to understand the trade-offs and make decisions based on this. Of course, the only way to really know which choice is right for you is to instrument your code!

pic1


Namespaces

The example you saw above introduced some new syntax that you haven’t encountered before — the double colon – ::. This is how you reference scope within C++; it’s used above to tell the compiler where it should look for the foo function.

Another time you’ll see the double colon is when using namespaces. Namespaces are a way to separate code so that naming clashes are less likely to occur.

For example, you might implement a class called Person in your own code, but a third party library may also implement a class called Person. Therefore, when writing C++ you will usually put all your code into a namespace to avoid these types of naming collisions.

It’s easy to do this; you just wrap everything with the following namespace declarations:

namespace MyNamespace {
    class Person {};
}
 
namespace LibraryNamespace {
    class Person {};
}

Now, when using either implementation of the Person class, you can disambiguate using the double colon, like so:

MyNamespace::Person pOne;
LibraryNamespace::Person pTwo;

Simple, isn’t it?

There is no equivalent to namespaces in Objective-C, other than by the convention of placing a prefix on your classes…you do name your classes like this, right? ;] Well if you don’t, then this is a perfect reason to start doing it straight away!

Note: There have been a few proposals for namespaces in Objective-C. One such proposal can be found here. I don’t know if we’ll ever see them in Objective-C, but I sure hope so myself!

Memory Management

Oh no… not that dreaded phrase! Memory management is one of the most important things to understand in any language. Java basically lets the garbage collector do its job. Objective-C requires that you understand reference counting and the role that ARC plays. In C++… well, C++ is a different beast once again.

First of all, to understand memory management in C++ you really need to understand the stack versus the heap. Even if you think you know about this, I suggest you carry on reading anyway; you’ll probably learn a thing or two.

  • The stack is a block of memory available to a running application. It’s a fixed size and is used by the application’s code to store data. The stack works on a push/pop basis; when a given function runs it pushes data onto the stack, and when a function finishes, it must pop off the same amount of data. Therefore, over time the stack usage will not grow.
  • The heap is also a block of memory available to a running application. It is not of any fixed size, and grows as the application runs. This is where applications tend to store data that’s used outside of a function’s scope. Also, large sets of data will usually be stored in the heap because storing it on the stack may overflow the stack — remember, the stack is a fixed size.

That’s a very brief overview of stack and heap theory; here’s some C code that shows the two in action:

int stackInt = 5;
int *heapInt = malloc(sizeof(int));
*heapInt = 5;
free(heapInt);

Here, stackInt is using stack space; after the function returns, the memory being used to store the value “5” is automatically freed.

Hoever, heapInt is using heap space. The call to malloc allocates enough space on the heap to store an int. But because heap has to be managed by you, the developer, there needs to be a call to free after you’ve finished with the data to ensure you’re not leaking memory.

In Objective-C, you can only create objects on the heap; you’ll receive a compiler error if you try to create an object on the stack. It simply won’t work.

Consider the following example:

NSString stackString;
// Untitled 32.m:5:18: error: interface type cannot be statically allocated
//         NSString stackString;
//                  ^
//                  *
// 1 error generated.

That’s why you see asterisks all over Objective-C code; all objects are created on the heap and you have pointers to those objects. This largely comes down to the way that Objective-C handles memory management. Reference counting is very much baked-in to Objective-C; objects need to be on the heap so that their lifetime can be tightly controlled.

In C++ you can decide to either store data on the stack or the heap; the choice is up to you as a developer. However, in C++ you also have to manage the memory all by yourself. Putting data on the stack is taken care of automatically, but when you start using the heap you have to handle the memory management yourself — or risk springing leaks all over the place.

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.

pic2

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.

Where to Go From Here?

In this first part of the series you’ve learned a brief history of C++, how to declare a class and how memory management works in C++. Of course, there is a lot more to the language than this!

In the second part of the series, you’ll learn about more advanced class features and templates before taking a look at the standard library and Objective-C++.

In the meantime, if you have any questions or comments about your adventures with C++, please join the discussion below!



转自:http://www.raywenderlich.com/62989/introduction-c-ios-developers-part-1

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值