Professional Documents
Culture Documents
The most common use for a class reference is to create objects or to test the
type of an object reference, but you can use class references in many other
situations, including passing class references as routine parameters or
returning a class reference from a function. The type of a class reference is
called a metaclass.
Objects
An object is a dynamic instance of a class. The dynamic instance
contains values for all the fields declared in the class and all of its
ancestor classes. An object also contains a hidden field that stores a
reference to the object's class.
Objects are always allocated dynamically, on the heap, so an object
reference is really a pointer to the object. The programmer is
responsible for creating objects and for freeing them at the
appropriate time. To create an object, use a class reference to call a
constructor, for example:
Obj := TSomeClass.Create;
Most constructors are named Create, but that is a convention, not
a requirement of Delphi. You will sometimes find constructors with
other names, especially older classes that were written before
Delphi had method overloading. For maximum compatibility with
C++ Builder, which does not let you name constructors, you should
stick with Create for all your overloaded constructors.
To get rid of the object when your program no longer needs
it, call the Free method. To ensure that the object is
properly freed, even if an exception is raised, use a try-
finally exception handler.
Obj := TSomeOtherClass.Create;
try
Obj.DoSomethingThatMightRaiseAnException;
Obj.DoSomethingElse;
finally
Obj.Free;
end;
When freeing a global variable or field, always set the variable to nil
when freeing the object so you are not left with a variable that
contains an invalid pointer. You should take care to set the variable
to nil before freeing the object. If the destructor, or a method called
from the destructor, refers to that variable, you usually want the
variable to be nil to avoid any potential problems. An easy way to
do this is to call the FreeAndNil procedure (from the SysUtils
unit):
GlobalVar := TFruitWigglies.Create;
try
GlobalVar.EatEmUp;
finally
FreeAndNil(GlobalVar);
end;
Inheritance
A class can inherit from another class. The derived class
inherits all the fields, methods, and properties of the base
class. Delphi supports only single inheritance, so a class has
one base class. That base class can have its own base class,
and so on, so a class inherits the fields, properties, and
methods of every ancestor class. A class can also implement
any number of interfaces (which are covered later in this
chapter). As in Java, but not C++, every class inherits from a
single root class, TObject. If you do not specify an explicit
base class, Delphi automatically uses TObject as the base
class.
TIP:
A base class is a class's immediate parent class, which you can see
in the class declaration. An ancestor class is the base class or
any other class in the inheritance chain up to TObject. Thus, in
the first example, TCertificateOfDeposit has a base class of
TSavingsAccount; its ancestor classes are TObject,
TAccount, and TSavingsAccount.
Delphi retains the strong type-checking of Pascal, so the
compiler performs compile-time checks based on the declared
type of an object reference. Thus, all methods must be part of
the declared class, and the compiler performs the usual checking
of function and procedure arguments. The compiler does not
necessarily bind the method call to a specific method
implementation. If the method is virtual, Delphi waits until
runtime and uses the object's true type to determine which
method implementation to call. See the section "Methods," later
in this chapter for details.
Use the is operator to test the object's true class. It returns True
if the class reference is the object's class or any of its ancestor
classes. It returns False if the object reference is nil or of the
wrong type. For example:
if Account is TCheckingAccount then ...
// tests the class of Account
if Account is TObject then ...
// True when Account is not nil
You can also use a type cast to obtain an object
reference with a different type. A type cast does not
change an object; it just gives you a new object
reference. Usually, you should use the as operator for
type casts. The as operator automatically checks the
object's type and raises a runtime error if the object's
class is not a descendant of the target class. (The
SysUtils unit maps the runtime error to an
EInvalidCast exception.)
Another way to cast an object reference is to use the
name of the target class in a conventional type cast,
similar to a function call. This style of type cast does
not check that the cast is valid, so use it only if you
know it is safe, as shown in next example
Example : Using Static Type Casts
var
Account: TAccount;
Checking: TCheckingAccount;
begin
Account := Checking; // Allowed
Checking := Account; // Compile-time error
Checking := Account as TCheckingAccount; //
Okay
Account as TForm; // Raises a runtime error
Checking := TCheckingAccount(Account); // Okay,
but // not
recommended
if Account is TCheckingAccount then // Better
Checking := TCheckingAccount(Account)
else
Checking := nil;
Fields
A field is a variable that is part of an object. A class can declare any
number of fields, and each object has its own copy of every field declared
in its class and in every ancestor class. In other languages, a field might be
called a data member, an instance variable, or an attribute. Delphi does not
have class variables, class instance variables, static data members, or the
equivalent (that is, variables that are shared among all objects of the same
class). Instead, you can usually use unit-level variables for a similar effect.
A field can be of any type unless the field is published. In a published
section, a field must have a class type, and the class must have runtime
type information (that is, the class or an ancestor class must use the $M+
directive).
When Delphi first creates an object, all of the fields start out empty, that is,
pointers are initialized to nil, strings and dynamic arrays are empty,
numbers have the value zero, Boolean fields are False, and Variants are
set to Unassigned.
A derived class can declare a field with the same name as a field in an
ancestor class. The derived class's field hides the field of the same name in
the ancestor class. Methods in the derived class refer to the derived class's
field, and methods in the ancestor class refer to the ancestor's field.
Methods
Methods are functions and procedures that apply only to objects of a
particular class and its descendants. In C++, methods are called
"member functions." Methods differ from ordinary procedures and
functions in that every method has an implicit parameter called Self,
which refers to the object that is the subject of the method call. Self is
similar to this in C++ and Java. Call a method the same way you
would call a function or procedure, but preface the method name with
an object reference, for example:
Object.Method(Argument); A class method applies to a class and its
descendants. In a class method, Self refers not to an object but to the
class. The C++ term for a class method is "static member function."
You can call a method that is declared in an object's class or in any of
its ancestor classes. If the same method is declared in an ancestor class
and in a derived class, Delphi calls the most-derived method, as shown
in the followiong example
Example : Binding Static Methods
Type
TAccount = class
public
procedure Withdraw(Amount: Currency);
end;
TSavingsAccount = class(TAccount)
public
procedure Withdraw(Amount: Currency);
end;
var
Savings: TSavingsAccount;
Account: TAccount;
begin
...
Savings.Withdraw(1000.00); // Calls TSavingsAccount.Withdraw
Account.Withdraw(1000.00); // Calls TAccount.Withdraw
An ordinary method is called a static method because the
compiler binds the method call directly to a method
implementation. In other words, the binding is static. In C++
this is an ordinary member function, and in Java it's called a
"final method." Most Delphi programmers refrain from using
the term static method, preferring the simple term, method or
even non-virtual method.
A virtual method is a method that is bound at runtime instead of
at compile time. At compile time, Delphi uses the declared type
of an object reference to determine which methods you are
allowed to call. Instead of compiling a direct reference to any
specific method, the compiler stores an indirect method
reference that depends on the object's actual class. At runtime,
Delphi looks up the method in the class's runtime tables
(specifically, the VMT), and calls the method for the actual
class. The object's true class might be the compile-time declared
class, or it might be a derived class--it doesn't matter because
the VMT provides the pointer to the correct method.
This is the End of the
presentation!
Thank you!