Delphi Object Model (Part Vi)

zhaozj2021-02-11  205

Delphi Object Model (Part Vi)

Delphi is rich and powerful for object-oriented programming. In addition to traditional classes and objects, Delphi also provides features such as interface, abnormal processing, multi-threaded programming. This chapter explains the object model of Delphi. The reader should be familiar with the standard Pascal and have a certain understanding of the basic law on object-oriented programming.

(The original text of this article will decompose Delphi and Object Pascal as Delphi, may have a conceptual suspected. However, in most cases, I believe that readers can determine the specific meaning of Delphi described in the text according to context - Translator's Note .)

Interfaces interface

The interface defines a type containing a set of abstract methods. A class, even from a simple base class, you can also achieve any more excuses. The interface is similar to the abstract class (ie there is no field and all methods are class of abstract methods), and Delphi provides additional features. Delphi interface Sometimes it is very like COM (Component Object Model) excuse, but the interface using Delphi does not need to know about COM, and you can also use the interface as many other purposes.

You can declare a new interface - it inherits in an existing interface. The declaration of the interface contains the declarations of methods and properties, but there is no field. As all classes are inherited in TOBJECT, all interface classes are inherited from IunkNown. Interface IUNKNOWN defines three ways: _ADDREF, _RELEASE, and QueryInterface. If you are familiar with COM, it will not be unfamiliar. The first two methods are used to manage the lifecycle reference count of the object that implements this interface. The third method is used to access other interfaces that the object may be implemented.

When you want to declare a class that implements one or more interfaces, you must implement all the methods declared in the interface. The new class can directly implement the interface, or you can delegate this implementation to an attribute - its value is an interface. The easiest way to implement _addref, _release, and queryinterface method is to inherit TinterFaceDObject and its derived approach, of course, you can also inherit from other classes. If you want your own way.

New classes must be used in the method name, parameters, and calling conventions that are consistent with the interface method when implementing the interface. Delphi automatically pairs the method of the class with the corresponding method of the interface. If you want to use different method names, you can use different method names to redirect the interface. The method used as a redirection must have the consistent parameters and call conventions for the method of the interface. This feature is very important, and when a class needs to implement multiple interfaces, it is especially true when there are repetitive methods. Find the keyword class in Chapter 5 to get more content related to the redirection method.

Classs can use the Implements indicator to entrust the interface's implementation to an attribute. The value of this property must be the type of interface that this class will implement. When the object is mapped to the interface, Delphi automatically obtains the value of the attribute and returns the interface. Refer to the contents of the IMPLEMENTS indicator for reference to Chapter 5.

For each non-entrust-mode implementation, the compiler creates an implicit field for stamping the VMT to the interface. The field of the interface is just after the object implicit VMT field. As the object reference is actually a pointer to the implied VMT field of the object, the reference to the interface is also a pointer to the implicit VMT field. Delphi automatically initializes implies fields when the object is created. Refer to Chapter 3 about how the compiler uses RTTI to track the contents of VMT and implicit fields.

Reference country counting count

The compiler triggers the call to _addref and _Release to manage the life cycle of the interface object. To use the Delphid's automatic reference count, declare a variable of an interface type. When you assign an interface reference to an interface variable, Delphi automatically calls _addref. Delphi automatically calls _RELEASE when changing the variable amount away.

_Addref and _Release behavior depends entirely on you. If you inherit from TinterFaceDObject, these methods complete the reference to the reference count. _Addref method is used to increase the reference count, _Release is used to reduce the reference count. When the reference count is 0, the _RELEASE method will release the object. If you inherit from other classes, you can define your own way. However, you should correct the queryinterface method correctly because Delphi is based on this to implement AS operations. TYPECASTING type conversion

Delphi calls QueryInterface to implement part of the AS operations on the interface. You can use the AS operator to convert an interface to another interface. Delphi calls QueryInterface to get a new interface reference. If QueryInterface returns an error, the AS operation will trigger a running period error. (This runs in the SYSUTILS unit are mapped to the EintFCastError exception class.)

You can use your own way to implement queryinterface methods, although you may tend to be close to the implementation of TinterFaceDObject. Example 2-13 shows a class that implements a normal QueryInterface method, but the implementation of _addref and _Release methods is greatly different. You will see what is used in this way later.

Example 2-13: Interface classes do not need to be referenced

Type

TNOREFCOUNT = Class (TOBJECT, IUNKNOWN)

protected

Function QueryInterface (Const IID: Tguid; Out Obj): hResult; stdcall;

Function _addref: integer; stdcall;

Function _Release: integer; stdcall;

END;

Function Tnorefcount.QueryInterface (Const IID: Tguid; Out Obj): hResult;

Begin

IF GetInterface (IID, OBJ) THEN

Result: = 0

Else

Result: = windows.e_nointerface;

END;

Function TNOREFCOUNT._ADDREF: Integer;

Begin

Result: = -1

END;

Function TNOREFCOUNT._RELEASE: Integer;

Begin

Result: = -1

END;

Interfaces and Object-Oriented Programming interface and object-oriented programming

The most important role of the interface is to separate the type inheritance and class inheritance. Class inheritance is an effective tool for code reuse. The derived class is easy to inherit the field, method, and attributes of the base class, and do not need to re-implement common methods. In a strong type of language, such as Delphi, the compiler regards a class as a type, so the concept of class inheritance and type inheritance seems to be a bit overlap. But as far as possible, we should strictly distinguish between types (TYPE) and classes.

Many books related to object-oriented programming are described as "Yes", such as a TSAVINGSACCOUNT "is Taccount. You can experience the same meaning when you use Delphi's IS operator to test whether an Account variable is Tsavingsaccount.

The simple "YES" in the above example is no longer adapted. The square belongs to a rectangle, but this does not mean that you are willing to inherit TSQUARE from TRECTANGLE. The rectangle belongs to a polygon, but you may not want TRECTANGLE to inherit from TPOLYGON. Class inheritrates all the fields declared in the class save the base class, but in this case, the derived class does not need this information. A TSQUARE object simply saves a single length of all of it. However, a TRECTANGLE object must save two lengths. A TPOLYGON object requires saving a number of edges and vertices. The solution is to inherit it from class inheritance (class C inheritab the fields and methods, and B inherit the field and method of the A) separation into type inheritance (the square is a rectangle, the rectangle is also a polygon). Use the interface to implement type inheritance, you can make the class inheritable: the inheritance of the fields and methods.

In other words, isquare inherits from IRECTANGLE, while the latter is inherited from IPOLYGON. The interface follows the relationship of "Yes". Fully interfaced from the interface, class TSQUARE implements interface isquare and IRECTANGLE and IPOLYGON. TRECTANGLE implements IRECTANGLE and IPOLYGON.

Tip: One agreement for COM programming is to name I of the name of the interface. All interfaces of Delphi follow this agreement. Note that this is just a useful agreement, not a mandatory requirement.

In terms of implementation, you can declare the class to reuse the purpose of the code reuse. For example, use TBaseShape to implement public fields and methods for all shapes. TRECTANGLE inherits from TBASESHAPE and then implements the corresponding method with the characteristics of the rectangle. The polygon still inherits from TBASESHAPE, and achieves the corresponding method according to the characteristics of the polygon.

A drawing program can operate an iPolygon interface to use a variety of shapes. Example 2-14 shows a simple class and interface based on this idea. Note that each interface also declares the GUID (globally unique identifier). GUID is required when using queryinterface. If you want to use the GUID of the interface, you can directly use the name of the interface. Delphi automatically converts the name of the interface to the corresponding GUID.

Example 2-14: Separation type and class inheritance

Type

ISHAPE = Interface

['{50f6d851-f4eb-11d2-88ac-00104bcac44b}']

Procedure Draw (Canvas: Tcanvas);

Function getPosition: tpoint;

Procedure setPosition (Value: tpoint);

Property Position: Tpoint Read getPosition Write setPosition;

END;

Ipolygon = interface (ishape)

['{50F6D852-F4EB-11D2-88AC-00104BCAC44B}']

Function Numvertices: integer;

Function Numsides: Integer;

Function SideLength: Integer;

Function Vertex (INDEX: Integer): TPOINT;

END;

IRECTANGLE = Interface (IPOLYGON)

['{50F6D853-F4EB-11D2-88AC-00104BCAC44B}']

END;

Isquare = interface (IRECTANGLE)

[{50f6d854-f4eb-11d2-88ac-00104bcac44b} ']

Function Side: Integer;

END;

TBASESHAPE = Class (Tnorefcount, IShape) Private

FPSITION: TPOINT;

Function getPosition: tpoint;

Procedure setPosition (Value: tpoint);

public

Constructor create;

Procedure Draw (canvas: tcanvas); virtual; abstract;

Property Position: Tpoint Read fposition Write setPosition;

END;

TPOLYGON = Class (TBASESHAPE, IPOLYGON)

Private

Fvertices: array of tpoint;

public

Procedure Draw (Canvas: Tcanvas); OVERRIDE;

Function Numvertices: integer;

Function Numsides: Integer;

Function SideLength: Integer;

Function Vertex (INDEX: Integer): TPOINT;

END;

TRECTANGLE = Class (TBASESHAPE, IPOLYGON, IRECTANGLE)

Private

FRECT: TRECT;

public

Procedure Draw (Canvas: Tcanvas); OVERRIDE;

Function Numvertices: integer;

Function Numsides: Integer;

Function SideLength: Integer;

Function Vertex (INDEX: Integer): TPOINT;

END;

Tsquare = Class (TBASESHAPE, IPOLYGON, IRECTANGLE, ISQUARE)

Private

Fside: integer;

public

Procedure Draw (Canvas: Tcanvas); OVERRIDE;

Function Side: Integer;

Function Numvertices: integer;

Function Numsides: Integer;

Function SideLength: Integer;

Function Vertex (INDEX: Integer): TPOINT;

END;

The derived class inherits the interface of the ancestral class. TRECTANGLE inherits from TBASESHAPE, then TBaseShape implements the ISHAPE interface is TRECTANGLE implementation ISHAPE interface. The inheritance of the interface is somewhat different. The inheritance of the interface is only for the convenience of the type, that is, you don't have to re-enter the statement of many methods. When a class implements an interface, it does not mean that the class automatically implements ancestor interface. In fact, this class only implements these interfaces that appear in this class (and interfaces appear in the statement of the ancestral class). Therefore, even if IRECTANGLE is inherited from IPOLYGON, the TRECTANGLE class has to list IRECTANGLE and IPOLYGON explicitly.

To implement a type system, you should not use the reference count. Instead, you need to achieve explicit memory management, just like processing a normal Delphi object. In this case, the best way to implement _addref and _release methods is the root of the root, just like the TnorefCount class we have seen in Example 2-13. It is also necessary to note that do not have any variables to point to the invalid reference. An object reference that has been released may lead to a problem because Delphi will automatically call the _Release method. That is, do not try to use the variable to point to the invalid pointer, use the interface without using the reference count to force you to do this. Parti Parti Partiii Partiv Partv Partvi More Articles

转载请注明原文地址:https://www.9cbs.com/read-5323.html

New Post(0)