您的位置:首页 > 移动开发 > Objective-C

C++ vs Objective C

2013-01-01 13:24 357 查看
Are you a game developer transitioning from Cocos2d iPhone to Cocos2d-X? From Objective C to C++?
C++ used to be my language. It was all I wrote computer games in for over 10 years. There's a certain level of power, control and efficiency in using C++.

When the iPhone came out, the Objective C language became a hot topic. I was so excited about the thought of writing games for iPhone that I forced myself, despite wariness at
[[funny foreign] syntax]
, to learn Objective C and begin writing a game. Boy was I surprised when I came to love Objective C's simple elegance and powerful Foundation classes.

Now, after nearly three years of making games in Objective C with Cocos2D-iPhone, I've discovered Cocos2D-X and unearthed a whole new love for C++.

C++ is a Manual Transmission Car




If Objective C is an automatic transmission car, then C++ is a manual.

One might argue that the automatic car is superior because it's easier. However, that one hasn't had the experience of clutching into neutral across a gravelly patch to avoid spinning the tires.

So, C++ has more speed and control, yet it requires a greater level of skill from the programmer.

For example, C++ saves execution time by not checking pointers before you dereference them. This increases the speed of your game and your level of control, yet it requires basic assertion skills.

Sending Messages to nil Objects (or Dereferencing null Pointers)

Here we have one of the most fundamental differences between ObjC and C++.

With Objective C, you can safely send messages to nil objects (though this comes at the cost of slightly slower program execution).

// in Objective C
CCNode* node = nil;

// this will not crash, it will just do nothing
[node setPosition:ccp(10,10)];

In C++, if you try to dereference a null pointer, you'll trigger a runtime error and have to fix your code up.

// in C++
CCNode* node = nullptr; // nullptr introduced in C++11

// this will crash
node->setPosition(ccp(10,10));

// using a safeguard, this will not crash
if(node)
node->setPosition(ccp(10,10));

// using an assertion will trigger a debuggable error
assert(node);
node->setPosition(ccp(10,10));

Behind the scenes, ObjC basically implements a nil object which essentially
returns zero for any method call. This makes programming a little safer and easier. However, it also can allow bugs to go unnoticed.




C++ doesn't check a pointer before dereferencing it. As illustrated above, if the node is null, then C++ will execute the setPosition function given a null
this
pointer. When a member variable is accessed via the null
this
pointer, your program crashes.

One solution is to use an
assert which only gets compiled into non-distribution builds. If the expression being asserted evaluates to false, then the program aborts. If you've got a debugger attached, then it will pause on the assert line so you can see exactly where the code went
wrong.

CCNode* node = nullptr;

// this will pause execution when debugging
assert(node);

Since assert is typically defined as a macro, one of the pros to using it is that in distribution builds (or any build which defines NDEBUG), the macro evaluates to nothing. Essentially, the compiler doesn't even include it. This gives you the security of
assertions in debug builds and the raw speed of no assertions in distribution builds.

Essentially this makes C++ a little more efficient and a bit easier to catch bugs at the cost of the programmer having to be a bit more pragmatic.

Readability

Here's another fundamental difference. Objective C is inherently more readable than C++ because method parameters are built into the name of the method. Consider:

// Objective C
[layer addChild:sprite z:1];

// C++
layer->addChild(sprite, 1);

You see the difference in readability?

Objective C is a bit easier to read because the 2nd parameter to the addChild method is clearly a z value. In C++, it's anyone's guess what "1" means.

Modern day Integrated Development Environments (IDEs) like Xcode mitigate the issue when you are writing code because of auto-completion. You start typing addChild and it gives you the parameter types and names. This makes it clear what the parameters are
when you are writing the code. However, if you take some time off from the project and come back later, you might be a little perplexed when you glance at that addSprite call and wonder about the second parameter.

You can make C++ more readable by changing up your style a little bit. Like this:

int z = 1;
layer->addChild(sprite, z);


Speed and Portability




We've already talked about how C++ is a
faster than Objective C. But what about portability? This is where C++ shines.

C++ is simply more portable than Objective C. It can be natively compiled on many platforms including the presently popular Android OS.

When it comes to Android, it is technically possible to compile ObjC if you upgrade the default NDK's GCC compiler, but there's a catch or two. You need all those Foundation classes.

Remember
NSObject
,
NSString
and
NSArray
? Those are all implemented fully and awesomely on the Mac, but not Windows. All those have to be reimplemented in a cross-platform way.

Thankfully, there's projects like
Cocotron which aim to do just that. However, when it comes down to areas that are Android-specific, like input and sound, you need a cross-platform library. Something that implements that stuff for you so you don't have to go rewriting it for each and every
platform.

That's why C++ and Cocos2dx are so awesome. Cocos2dx handles the platform-specific stuff and does the bulk of it in portable C++.

Beyond speed, portability and readability, we get into more specific differences between the languages, like Objective C's Class and
id
types.

The Class and id Types

Objective C has a nifty thing called the Class type. It allows you to take the class of an object and store it in a variable.

C++ doesn't have the equivalent of a Class type. It does have runtime type information (RTTI) and can roughly convert a pointer into a string containing the object's class with

typeid. However, since the results can vary per platform, the solution is not exact enough to be relied upon.

Objective C's Class type makes creating objects from strings quite nice. Consider the following:

// Objective C
NSString* string = @"Hammer";
Class class = NSClassFromString(string);
id object = [[class alloc] init];

// C++
Tool* tool = nullptr;
std::string string = "Hammer";
if( string == "Hammer" )
tool = new Hammer;

This illustrates the elegance of ObjC's Class type and one possible workaround in C++.

You can also see in the above code we are using ObjC's
id
type. It is essentially a pointer to any object type. The rough equivalent in C++ is
void*
, however there are many
reasons not to use either.

Laziness

Let's say we have a parent class called Parent, a subclass of it called Child, and Parent has a method called goToSleep.

In C++, you have to redefine goToSleep in Child's interface if you want to override. In Objective C, you can just be lazy and override it in the implementation.

// Objective C
@interface Parent : NSObject
-(void) goToSleep;
@end

@interface Child : Parent
// woo! don't have to redefine goToSleep
@end

@implementation Parent
-(void) goToSleep {}
@end

@implementation Child
-(void) goToSleep {}
@end

// C++
class Parent
{
public:
virtual void goToSleep();
};

class Child : public Parent
{
public:
// goToSleep has to be redefined
// if it is to be overridden
virtual void goToSleep();
};

void Parent::goToSleep() {}

void Child::goToSleep() {}

The reason C++ forces a programmer to redefine functions that will be overridden is because it makes the job of the compiler easier. The compiler doesn't have to go checking up the class hierarchy for mystery methods.

Virtual




In the above example, we saw the use of the C++
virtual
keyword. When
virtual
is used, it specifies to use the function in the lowest class of the hierarchy first (for example Child's goToSleep as opposed to Parent's goToSleep).

In Objective C, all method calls are essentially virtual. This makes it a bit easier on the programmer, but it comes at a possible
performance decrease.

Again, C++ comes through on the manual transmission car analogy. You can specify virtual or non-virtual, as appropriate.

The Stack

In Objective C, you can only allocate objects from the heap.

In C++, you can allocate objects on the stack. Stack allocation is
faster than heap allocation and also guarantees that the
destructor will be called when the object goes out of scope, even if an exception has been thrown.

Here's an example:

// Objective C
// Heap allocations only
Tool* tool = [[Tool alloc] init];

// C++
// Sweet! We've got fast, reliable stack allocations
Tool tool;

Operator Overloading

In C++ you can overload operators. For example, if you were programming a Vector object, you could overload the addition operator so that Vectors could naturally be added together.

// C++
class Vector
{
const Vector operator+(const Vector& v) const
{
return Vector(x + v.x, y + v.y, z + v.z);
}
}

// this '+' calls the overloaded operator+
// isn't it nice?
Vector v1,v2,v3;
v3 = v1 + v2;

In Objective C, it's
not possible to overload operators. You can workaround that by creating worded methods:

// Objective C
v3 = [v1 addVector:v2];

However, this makes Objective C a bit wordy when it comes to some things:

// Objective C
NSString* a,*b;
if( [a isEqualToString:b] )
[self doSomething];

// C++
std::string a,b;
if( a == b )
doSomething();

Privates

Objective C doesn't technically have private methods. However, the workaround using a blank category in the implementation is quite nice because the private method doesn't have to be declared in the public interface (and hence your project automatically
re-indexed because of a header file change):

// In an Objective C header file (.h)
@interface Something : NSObject
// See? No private method
@end

// In the implementation file (.m)
// Extend the Something interface with a category
// (Blank categories denote private stuff)
@interface Something () // a blank category
-(void) privateMethod;
@end

In C++, there is the concept of public and private, however the privates have to be declared in the public interface.

If you really don't want to declare a private function in the public interface, you can work around by using static functions in the implementation (which is similar to the Objective C blank category strategy mentioned above).

Class Extensions

As we saw in the above example, Objective C can extend a class with a category.

Categories are pretty neat because a class can be extended anywhere, including the .h, the .m or some other place.

C++ doesn't technically have the ability to extend a class. However, it does have

multiple inheritance.

Default Parameters

C++ has one final bit of coolness to discuss: default parameters. A parameter to a function can be given a default, so that supplying it when calling the function is optional.

// C++
class Something
{
void doSomething(int i, float f = 0.0f) {}
};

Something s;

// can be called with or without the f parameter
s.doSomething(5);
s.doSomething(5, 1.0f);

Objective C doesn't have default parameters, but it can get by with a multiple method workaround:

// Objective C
@interface Something : NSObject
-(void) doSomething:(int)i;
-(void) doSomething:(int)i withF:(float)f;
@end

@implementation Something
-(void) doSomething:(int)i
{
[self doSomething:i withF:0.0f];
}

-(void) doSomething:(int)i withF:(float)f
{
}
@end

Something* s = [[Something alloc] init];

// either method can be called, however
// two separate methods have been implemented
[s doSomething:5];
[s doSomething:5 withF:1.0f];

Conclusion: Mixing C++ & ObjC

So that's about it for the primary differences between ObjC and C++.

Keep in mind that you can always use both. In fact, you can mix them together with
Objective C++.

You just rename your implementation file with the .mm extension. That tells your compiler to use Objective C++ and enables either Objective C, C++ or a mix of both to be used.

// In the file AppDelegate.mm
@implementation AppDelegate
-(void) applicationDidFinishLaunching:(NSNotification*)aNotification
{
// Do some Objective C stuff
window = [[Window alloc] initWithContentRect:...];

// Now switch to C++ and launch Cocos2dx
cocos2d::CCApplication::sharedApplication().run();
}
@end

Objective C++ is a common thing. As you can see in the example above, it's required to respond to iOS & Mac app notifications and launch Cocos2d-X in the same method.

Got questions? Leave a comment below.

Stay tuned. We are writing the next chapter in this free online book. If you'd like, you can

subscribe to be notified when we release new chapters.
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: