Reproduced to: http://tech.ccidnet.com/art/1077/20040523/643853_1.html
If you are new to Delphi, you may be most interested in its rich and powerful VCL (visual component library ). It's exciting to simply throw several components into the form and easily create a program with practical value without having to write code. However, VCL is only a small part of Delphi, and Delphi is far better than VCL. If you are only using VCL, you will never be a real expert in Delphi. Remember, you must go beyond VCL to get access to the core of Delphi.
So what is hidden behind Delphi's VCL? This article will discuss two advanced topics of Delphi: OOP and database programming.
This article assumes that the reader has basic knowledge of Delphi programming, for example, familiar with the general syntax of Pascal, master the simple use of VCL, and use the Data-Ware component to write basic database programs. This article will not repeat the use of VCL.
1. OOP
The full name of OOP is Object Oriented Programming, which translates into object-oriented Programming. OOP is a very important programming idea. You may not be interested in such abstract things, but almost any expert will tell you: "language is not important, but programming is important ."
As you know, Delphi is Based on Object Pascal. This was developed after Borland added the object-oriented feature to the traditional Pascal language, and it is specially labeled with the Object to show the difference with the traditional Pascal Language, it can be seen that object-oriented technology has a great impact on it. It can be said that Delphi is built on Object Pascal, while Object Pascal is built on object-oriented technology.
In fact, not only Delphi, but also OOP is the basis of many other modern programming languages such as C ++ and Java (Visual Basic does not fully support OOP ). Mastering OOP is a necessary condition for in-depth understanding of Delphi, a must for entering the realm of mastery, and a sign of a mature programmer. After understanding the OOP technology, many things that have previously left you confused can be easily solved.
Interestingly, although Delphi is fully based on OOP, a programmer with no idea of OOP can also use Delphi to write programs, Because delphi will automatically complete most of the work. When you start to learn about Delphi, you may not be able to imagine how complicated it is to add a button to the form! But since we are interested in the world of Delphi and become a real programmer, we should be curious about the details of Delphi.
These theories may be boring and daunting. In fact, when you master it, you will feel nothing left. Of course, you need enough perseverance.
Oop has three main features:
1.1 Data encapsulation
Let's take a look at a piece of code:
Type
Tdate = Class
Mouth, day, year: integer;
Procedure setvalue (M, D, Y: integer );
Function leapyear: Boolean;
End;
First, we will see the class keyword, which is translated into "class" in Chinese ". Class is a very important concept. According to the authoritative definition, a class is a user-defined data type, which has its own instructions and some operations. A class contains some internal data and some object methods in the form of procedures or functions. It usually describes the common features and behaviors of some very similar objects.
This definition may be obscure. You can think of a class as a special record type, which may not only contain data, but also contain functions and processes (called methods in OOP ). These data and methods are collectively referred to as members of the class.
The above class is obviously a date type, which includes three data members: Mouth, Day, and Year, And the SetValue and LeapYear methods. By the way, in Delphi, we are used to using the letter T as the prefix of each class, just as in Viusal C ++, we are used to using the letter C as the prefix of each class.
The Mouth, Day, and Year data members specify the Year, month, and Day of the date. The SetValue method assigns values to the three data members, and LeapYear checks whether the year of the current date is a leap year. The following describes the implementation of these two methods:
Procedure TDate. SetValue (m, d, y): Integer;
Begin
Mouth: = m;
Day: = d;
Year: = y;
End;
Function TDate. LeapYear: Boolean;
Begin
If (Year mod 4 <> 0) then
LeapYear: = False
Else if (Year mod 100 <> 0)
LeapYear: = True
Else if (Year mod 400 <> 0)
LeapYear: = False
Else
LeapYear: = True;
End;
After these definitions are implemented, you can call them as follows:
Var
ADay: TDate;
Begin
// Create an object
Aday: = tdate. Create;
// Use
Aday. setvalue (1, 1, 2000 );
If aday. leapyear then
Showmessage ('leap year: '+ inttostr (aday. Year ));
// Release the object
Aday. Free;
End;
Let's explain the meaning of the code line by line. The line after var declares a variable of the tdate class.
How can we use a variable after it is declared? You can use the create method of the tdate class to create an object of this class and assign it to the aday variable.
Now we are exposed to another important concept in OOP: objects. What is an object? In short, an object is an instance of a class, or a variable of the data type defined by the class. When a class object is created, the system allocates a piece of memory for it. For example, if we define a variable A as the integer type, then integer is a data type and A is an instance. The relationship between classes and objects is similar to the relationship between the two. The difference between classes and objects is very important, and even some professional programmers often confuse them.
Careful readers may notice that the create method is not used in the definition of the tdate class. So where does the create method come from? The create method is an implicit method for every class. It is used to create an instance of this class. Note that classes and other data types are different here. Other data types can be directly used after the variables are declared, and the class type must be used after the create method is used to create its instance (object.
In fact, in C ++ and most other OOP languages, variables that declare a class can create objects of this class at the same time. Delphi (including its twin brother C ++ Builder) is different in this regard. You must Create a file to Create an object. At the same time, when this object is no longer needed, you must manually call the free Method to release this object (of course, the free method is also implicit in each class ). This is related to Delphi's unique "Object Reference Model". If you are interested, you can refer to the relevant materials.
This situation creates a very interesting phenomenon, that is, Programming beginners often forget to create it before using the object, which leads to errors, but the experts who have switched from C ++ to Delphi often make the same mistake ......
By the way, let us know that when the compiler encounters an error such as "Read of Address: ffffffff", it is probably because you forgot to Create before using the object. You can check the code from this aspect. In addition, do not forget to use free to release it when you do not need it, otherwise it may cause memory leakage.
The object code is used in the middle of the Code for creating and releasing objects. The data members of the access object are very simple and there is no difference with the Record type. You can use the dot expression to access them:
ADay. Year: = 2000;
ADay. Mouth: = 1;
ADay. Day: = 1;
Similarly, you can use the dot expression to call the object method. If you read the code for implementing the method, you can easily find that, ADay. setValue (2000,) is assigned to three data members respectively, and ADay. when LeapYear is called, whether the year of the current date is a leap year is returned. So far, the significance of the entire code segment is clear.
However, classes are not that simple. The above example is a very simple class that can directly access any of its members (data and methods ). However, some class members cannot be accessed at will. In Delphi, the access permissions of these members are distinguished by three keywords:
Table 1
Private |
Members of this type can only be accessed in the Declaration class. |
Public |
Members of this type can be accessed by code anywhere in the program. |
Protected |
Members of this type can only be accessed in the declared class and the declared class's derived class. |
The members of the protected type and the question of what is a "derived class" will be discussed later. Now we will focus on the first two.
The public type is the type in the preceding example. The private type can only be accessed in the class declared by the member (that is, the class to which the member belongs) according to the simple explanation in the table, it is invisible. So how will private members be used? Simply put, it is accessed through a public class method.
Let's look at a new example:
Type
Tdate = Class
Private
Mouth, day, year: integer;
Public
Procedure setvalue (M, D, Y: integer );
Function leapyear: Boolean;
Function gettext: string;
End;
In this class, the three Members mouth, day, and year are declared as private members, so they are not accessible outside of the class. That is, if you use
Aday. Year: = 2000;
If this code is used, the compiler will report an error. However, we can still assign values to them using the setvalue method:
Aday. setvalue (1, 1, 2000 );
This line of code is legal, because setvalue itself is a member of the tdate class, and it is also a public member. The gettext method can be used to obtain the current date value (this is the only way to obtain the current date value ).
This setting causes some members of the class to be hidden. Users can only use them in some special ways. Those members that can be accessed by external code are called class interfaces. What are the benefits of doing so? First, this allows the class author to detect the assigned content. For example, the user may assign an invalid date such as the 13-month-40-day to an object. After some Members are hidden, the class author can check whether these values are valid in the method code, thus greatly reducing the chance of generating errors. Second, the author can modify the code inside the class at any time using the standard class, but the code using this class does not need to be modified! This makes code maintenance a simple event, especially for large software that supports multi-person collaboration.
This is called Data encapsulation ). This is the first feature of OOP. An excellent OOP programmer should decide which important data should be encapsulated and give an efficient interface when designing the class.
It should be pointed out that the Private section in table 1 is completely correct for the "standard" OOP language (such as C ++), but there is an exception to Delphi. In Delphi, the Private member can be accessed in the Declaration class, and the unit (. can be accessed anywhere, regardless of the relationship between the Code and the declared class. Strictly speaking, this is against the OOP principle. I don't understand why Borland is doing this (it is said that it is for convenience ). In the discussion about the advantages and disadvantages of Delphi, This is a common problem.
1.2 inheritance and Derivation
Let's take a look at the Code:
Type
TNewDate = class (TDate)
Public
Function GetTextNew: String;
End;
Function GetText: String;
Begin
Return: = inttostr (Mouth) + ':' + inttostr (Day) + ':' + inttostr (Year );
End;
We can see that a class name is included in brackets after the class. This syntax indicates that the new class inherits an old class. The class that inherits the original class is called a derived class, also called a subclass, And the inherited class is called a base class or a parent class.
What is the relationship between a derived class and a base class? When a derived class inherits from a base class, it automatically has all the data, methods, and other types of the base class, without any further instructions in the derived class. For example, you can use the TNewDate class like the following code:
Var
ADay: TNewDate;
Begin
ADay: = TNewDate. create;
ADay. SetValue (1, 1, 2000 );
If ADay. LeapYear then
ShowMessage ('leap year: '+ Inttostr (ADay. year ));
ADay. free;
End;
In addition, the derived class can add its own data and methods on the basis of the base class. We can see that a new method GetTextNew is added to the TnewDate class. The implementation of this method is as follows:
Function GetTextNew: String;
Begin
Return: = GetText;
End;
Then call it:
ADay. GetTextNew;
This new method works well.
Why must the GetTextNew method call the GetText method in the base class instead of the Code in the GetText method? The reason is that the three Members Mouth, Day, and Year are declared as Private members, so they cannot be accessed even in the derived class. Therefore, the GetText method in the base class must be called, use them indirectly. If you want to use them directly, you can change the attributes of the three members from Private to Protected. In table 1, we can see that the members of the Protected attribute can be accessed in the declared class and the declared class's derived class. However, they cannot be accessed by other Code except the two cases. Now we can finally understand that this special attribute actually provides great convenience: it encapsulates the members of the class, avoids confusion, and allows the derived classes to conveniently use them.
(If you are a careful person, you may find a small flaw in the middle. When you actually access the Private member of the base class in the GetTextNew method, you may be surprised to find that the program can be compiled and run normally! In fact, this problem has nothing to do with OOP itself. As I have mentioned above, in Delphi, Private Members can be accessed anywhere in the unit file where the declared class is located. Therefore, if the TNewDate class and the TDate class are in the same. this is strange in the pas file .)
What do you think? With this inheritance mechanism, a class is no longer just an encapsulation of data and methods, but it provides openness. You can easily inherit a powerful class and add it to the features you need. At the same time, you do not need to modify the base class. On the contrary, any changes made by the original author to the base class can be immediately reflected in your new class. This meets the code reuse requirements.
This Inheritance Mechanism is also very consistent with the situation in the real world. It can be imagined that an "animal" is a class with its own characteristics (members), while a "dog" is a derived class of an "animal, it has all the characteristics of animals and its own unique characteristics (four legs, bark, etc ). The "dog" class can be further derived. For example, the "Black Dog" and "White Dog" have their own features (black color, white color, and so on ). A living dog can be regarded as an instance (object) of a "Black Dog" or "White Dog" (or another dog ).
The simulation of the real world by OOP not only greatly simplifies the maintenance of code, but also leads to revolutionary changes in the entire programming idea. It has made great strides over Modular programming.
If you have carefully read VCL materials and even its source code, you can find that the entire VCL is built on this powerful encapsulation-Inheritance Mechanism. You can see a detailed VCL hierarchy chart, which is like a huge family tree. Various VCL components are inherited by layers. For example, a simple TForm class is the product after many inheritance:
TObject-TPersistent-TConponent-TControl-TWinControl-TScrollingWinControl-TCustomForm-TForm
Not only does Delphi's VCL, the well-known MFC in Visual C ++ (Microsoft Foundation Class, Microsoft's basic Class Library), but also the OWL (Object Window Library, object window class library) is built on this mechanism. What's different is that for the first two languages, it takes several months for you to master the complex and incomparable classes to write useful programs, in Delphi, most of the work has been completed automatically by Delphi. For example, every time you add a form to a program, Delphi automatically derives a new class from TForm (TForm1 by default) and creates an instance for the new class. Your modifications to this form (adding components and Code) are nothing more than adding new features to this derived class; you no longer need to handle the situations of maximizing, minimizing, and changing the size, because these codes are implemented in the base class and inherited by the derived class. This is the greatness of Delphi. Of course, Delphi's VCL is no inferior to MFC or OWL (in fact it evolved from the latter ).
(Some may ask about VB. VB does not support inheritance, so there is no complicated class library, and its own controls are also very poor, mainly using ActiveX controls .).
If you have realized it, you may be confused about your findings. However, the things we want to discuss are certainly not that simple.
In part 2 ("Data encapsulation"), we mentioned that "the Create method is an implicit method for every Class ". In fact, this statement is not accurate. In Delphi, all classes are inherited from the most basic class TOject by default, even if you do not specify the inherited class name. The Create method is a method of the TObject class. Therefore, all classes automatically obtain the Create method, whether or not you have implemented it. If you don't have the Create method, how can you Create an object?
You may notice that the Create method is a special method. Yes, the Create method is really special. Even its "title" is not a function or procedure, but a Constructor (Constructor ). You can see the following examples in the source code of VCL:
Constructor Create;
The constructor is not only a Delphi keyword, but also a noun in the OOP methodology. In contrast, there is also the Destructor ). The former is responsible for creating an object and allocating memory for it. The latter is responsible for releasing the object and revoking its memory. Note that the Constructor name is generally Create, but the Destructor name is not Free, but Destroy. For example:
Destructor Destroy;
In the previous Code, why is Free used to release objects? The difference between the two is that Destroy will release the object directly, and Free will check whether the object exists. If the object exists or the object is not nil, it will call Destroy. Therefore, the program should try to use free to release objects, which is more secure. (Note that free does not automatically set the object to nil. Therefore, it is best to manually set the object to nil after free is called .)
You can also pass parameters to the constructor, as in a common function or process:
Type
TDate = class
Private
Mouth, day, Year: Integer;
Public
Function LeapYear: Boolean;
Function GetText: String;
Constructor Create (m, d, y: Integer );
End;
Procedure TDate. Create (m, d, y): Integer;
Begin
Mouth: = m;
Day: = d;
Year: = y;
End;
Call it:
ADay: TDate;
Begin
ADay: = TDate. create (1, 1, 2000 );
If ADay. LeapYear then
ShowMessage ('leap year: '+ Inttostr (ADay. year ));
ADay. free;
End;
In this way, the data Initialization is completed in the Create method without calling the SetValue method.
Next, we will discuss another important and interesting question: Virtual and overloaded methods.
Maybe you are a little dizzy ...... Let's take a look at a new example:
Type
TMyClass = class
Procedure One; virtual;
End;
Type
TNewClass = class (TMyClass)
Procedure One; override;
End;
Procedure TMyclass. One; virtual;
Begin
ShowMessage ('Call the TMyclass method! ');
End;
Procedure TNewClass. One; override;
Begin
Inherited;
ShowMessage ('Call the TNewClass method! ');
End;
We can see that a new class TNewClass is derived from TMyClass. Both classes declare One method with the same name. The difference is that in TMyClass, a Virtual keyword is added after the One Method, indicating that the Method is a Virtual Method ). In TNewClass, an Override keyword is added after the One method, indicating that the method is overloaded ). Heavy Load Technology can implement many special functions.
Let's carefully analyze their implementation. In the implementation Part Of The TMyclass. One method, a dialog box is displayed, indicating that the method has been called. There is nothing special here. In the TNewClass. One method, a statement has never appeared before:
Inherited;
The Chinese meaning of this word is "inherit ". For the moment, we should not involve complicated OOP concepts, as long as we know the functions of this statement. Its function is to call code in equivalent Virtual Methods in the base class. For example, if you use the following code:
Var
Aobject: tnewclass;
Begin
Aobject: = tnewclass. Create;
Aobject. One;
Aobject. Free;
End;
The program will pop up two dialog boxes. The first is to call the one method in the tmyclass class, and the second is the code in the tnewclass. One method.
The overload technology allows us not only to add data and methods not available in the base class to the derived class, but also to easily inherit the original method code of the base class, simply add inherited. If you do not add the inherited statement, the corresponding methods of the base class will be overwritten by the new method. However, you must note that the overload can only be performed when the base class method is marked as virtual, And the overloaded method must have the same parameter type as the virtual method.
There is also a special case of virtual methods, that is, abstract methods:
Procedure one; override; abstract;
After the one method, there is not only the override keyword, but also an abstract keyword (meaning abstraction ). This method is called an abstract method (called a pure virtual function in C ++ ). Classes that contain abstract methods are called abstract classes. Abstraction