According to Hoyle...
Objective-C
for C++ Programmers,
Part II
October 2008
by Jonathan Hoyle
jhoyle@maccompanion.com
macCompanion
http://www.jonhoyle.com
Last month,
we began our look at the Objective-C programming language from the perspective
of a C++ programmer. In
particular, we discussed class declarations and definitions as well as bracket
syntax for method invocation. This
month, we continue this investigation by looking at class construction and
destruction.
Class Instantiation in C++
When constructing an object in C++, two things take place: first
memory allocation, then object initialization (via a call to the class
constructor method). Typically,
these two things happen in the same call, so the C++ user does not have to
think about it. For example, let
us suppose we have a class named Foo which has a default constructor (that is, one
which takes no parameters). We can
create a new Foo object,
called foo in this way:
Foo *foo = new Foo;
In this case foo is a pointer to a newly
instantiated Foo object
allocated from the heap. If we
don't want to deal with pointers and are willing to allocated off the stack, we
can simplify this:
Foo foo;
In either case, memory has been reserved and foo's constructor, and any super class constructors,
have been called and completed. foo is now ready to use.
For constructors taking parameters, the syntax is as you would
expect:
Foo *foo1 = new Foo(parm1,
parm2);
Foo foo2(parm1, parm2);
If no constructors are declared at all, an implicit default
constructor assumed.
C++ constructors will implicitly call its super class constructors
in inheritance order, so that you needn't worry about it. Whether Foo is a subclass 10 generations deep, or no subclass
at all, you needn't worry.
C++'s convenience of combining allocation with initialization is
often underestimated, as it is a common practice in most other modern
languages. It is not until you use
a language lacking this convenience, such as Objective-C, do you come to fully
appreciate it.
Note: Advanced C++ users are aware of times when you wish to
decouple these steps. For example,
suppose you have previously allocated memory (stack or heap, it doesn't matter)
into which you wish to instantiate your object. Although this is relatively uncommon, C++ allows you to do
this straightforwardly as well. For example, if you wish to allocate a Foo object in an area of memory pointed at by ptr, you use the in-place new syntax:
foo
= new (ptr) Foo;
Class Instantiation in Objective-C
As suggested above, Objective-C treats memory allocation and class
initialization separately. For the
former case, it is the class alloc method, and it is heap based only (no stack based objects like in
C++). The syntax is simple enough:
Foo *foo = [Foo alloc];
An additional nicety is that the Objective-C alloc method zeroes out
the memory allocation for you. Now, since no initialization has been performed yet, the above line is analogous to the C++ lines:
Foo *foo = (Foo *) malloc(sizeof(Foo));
memset(foo, 0, sizeof(Foo));
That is, you now have a pointer to an allocation of cleared memory,
but you do not yet have a valid object. You now have to perform the next step of initialization. This is a class method that is usually
(but not always) called init. Traditionally, the name of any
initialization method begins with the word "init" (but is not required to be so). So typically the next line would look like this:
foo
= [foo init];
Now you will notice that the foo object has been reassigned after initialization. The reason for this is that a different
object may be returned after initialization, making the original pointer
invalid. So although simply
calling:
[foo init];
is syntacticly valid, the foo you are left with may be a dangling
pointer, and you can quickly become hosed using it. Objective-C users will typically chain their alloc and init
calls to achieve the same effect of C++ construction. Thus in one line, you often see:
Foo *foo = [[Foo alloc] init];
Just as with C++, Objective-C initializers may take parameters, as
in the following example:
Foo *foo = [[Foo alloc] initParm1:parm1 andParm2:parm2];
Note also that Objective-C initializers must call their super class
initialization methods explicitly, as there is no implicit initializer as there
is in C++.
Error Handling with Object Instantiation
In C++, the philosophy is essentially this: object construction is
assumed to succeed. A failure is
an exceptional situation, and therefore is typically managed by exception
handling. On modern machines,
memory allocation should almost never fail, as the hard drive is used for
virtual memory as need. In the
extreme situation where it does, the new operator will throw a bad_alloc exception.
Likewise, C++ constructors do not return a value, so they are always
assumed to succeed. If a C++
programmer wishes to indicate a failure to construct, he would throw an
exception to this effect.
In Objective-C, both alloc and init can fail,
and each one may return a NULL pointer in
such an instance. This is very
similar to the no throw syntax of C++:
foo
= new (std::nothrow) Foo;
For this reason, newly allocated object pointers must be checked for NULL.
Destructing Objects
Analogous to C++ construction, both object clean up and deallocation
all happen together during destruction. If
the object was created on the stack, then the programmer does
nothing...literally nothing...as destruction takes place as the object leaves
its scope. For objects that were
create on the heap with the new operator, an reciprocal call of delete is made,
as such:
delete foo; // Cleanup and
deallocate the object
And as expected, C++ inherited destructors are called in reverse
order of its construction, all prior to deallocation. Since foo is no longer a pointer to
valid data, it is common to see it assigned to NULL after its destruction. C++ destructors also quietly handle the case in which foo is already NULL, safely doing nothing.
As with C++, the Objective-C dealloc method is used for both cleanup and deallocation,
as so:
[foo dealloc]; // Cleanup and deallocate the object
That these two steps are combined for destruction, but not for
construction, is just one of those Objective-C peculiarities that you will
eventually grow used to.
Arrays of Objects
C++ also has the very nice feature of allocating arrays of objects,
as easily as it does single objects. Let us say for example, we wish to allocate say 10 Foo objects, not just one. Object construction and destruction look nearly identical to
their originals:
foo = new Foo[10]; // Construct an
array of 10 Foo's
... // Do stuff with these Foo's
delete [] foo; // Destruct
these Foo's
The additional brackets (required for both construction and
destruction).
Unfortunately, Objective-C has no such facility built into its
allocation method. Instead, an NSArray object must be constructed,
which is a bit more complicated. We will delve into that topic when we examine Objective-C container
classes. For now, this is where
we'll stop.
Coming Up Next Month: More Objective-C. See you
in 30!
To see a list of all the According to Hoyle columns,
visit: http://www.jonhoyle.com/maccompanion