According to Hoyle...
Objective-C for C++ Programmers, Part II
[ Part I | Part II ]
October 2008
by Jonathan Hoyle
jonhoyle@mac.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 the call to simply 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,
in an upcoming article. But for
now, this is where we'll stop, as we turn our attention to the C++ language.
Coming Up Next Month: C++ Language Updates. See you
in 30!
[ Part I | Part II ]
See a list of all the According to Hoyle columns
Mirror of:
http://www.maccompanion.com/macc/archives/October2008/Columns/AccordingtoHoyle38.htm