Research on constructor in Object Pascal Object Model

Source: Internet
Author: User

Http://www.delphi2007.net/delphiblog/html/delphi_2004511950333715.html

Preface
Recently, I have been using C ++ and Object Pascal (hereinafter referred to as OP) to learn more about Object-Oriented Programming (OOP ).

Speaking of OOP, I have already come into contact with this concept four years ago. Using Delphi as a development platform, the language is op,

At that time, I was at the initial stage of programming. I felt that Delphi was easy to learn. I dragged a few mouse clicks and put several visual controls on the form,

Add a few lines of code to complete a very beautiful Windows desktop program.

So at that time I thought this was the so-called Oop, so simple.

Now it seems that the idea at that time was somewhat naive. The implementation of these simple programs only shows that Delphi has powerful functions,

The Borland engineers who created it are great. The real OOP is still quite complicated!

In the days that followed, due to my passion for cracking and my friend's suggestions, I transferred my learning focus to ASM and C language learning.

Until today, I began to learn C ++, and found my thoughts were so thin.

Compared with procedural programming, oop is closer to the real world. You can even use classes to represent various physical objects in nature,

This reflects some features of OOP, such as encapsulation, inheritance, and polymorphism. Programming becomes more convenient, fast, and organized.

However, with the development of OOP, you will find that OOP is actually very complicated internally. In terms of code, when developing a class (such as a control ), you will know that it is not a simple job!

In terms of internal processing, any language of the OOP type, their compilers will do a lot of work behind the scenes for you.

Thanks to the above features, you will feel that OOP is a guy that makes you love and hate.

Here, I only use the op constructor as the starting point to talk about a small aspect of the Object Pascal object model. The right is my Object Pascal study notes.

Body

The best way to learn a language is to add Theory and Practice. When you are confused about the Code results, the best way to understand it is debug ).

The learning methods I mentioned today will be reflected in the subsequent discussions. Here I suppose you are familiar with OP syntax, Delphi usage, and ASM.

In Delphi, the source of all things is tobject. No matter whether your custom class indicates the inherited parent class, it must be the descendant of tobject, and it has all the features defined by tobject.

If you want to know what is going on with the constructor, start with it.

To view the source code of tobject, go to the \ Delphi installation path \ source \ RTL \ sys \ system. Pas file.

In tobject, you will find that the constructor is defined:

constructor TObject.Create;beginend;

 

Haha! Empty! This makes us embarrassed. How did the tobject and the inherited class instance be created?

It is certain that the compiler has done some background work for us. Let's see what it actually does?

For this reason, I specially designed a demo program to fully understand how the constructor works in the op object model.

First, you need to create a new console application in Delphi and replace the content in project1.dpr with the following code:

program Project1;{$APPTYPE CONSOLE}uses  SysUtils;type  TDerive = class( TObject )  public    constructor Create; overload;  private    x : integer;    y : double;  end;  TDerive1 = class( TDerive )  public    constructor Create( i : integer ); overload;  private    c : integer;  end;var  Obj : TObject;  Der : TDerive;  Der1 : TDerive1;  { TDerive }constructor TDerive.Create;begin  x := 7;  y := 0.1;end;{ TTDerive1 }constructor TDerive1.Create( i : integer );begin  inherited Create;  c := i;end;begin  Obj := TObject.Create;  Obj.Free;  Der := TDerive.Create;  Der.Free;  Der1 := TDerive1.Create( 5 );  Der1.Free;end.

Let's first look at how the simplest tobject instance is created.

In the Delphi ide Editor, set the breakpoint at line 1 and run it. In the context menu of the compiler, choose debug --> View CPU to bring up the debug CPU window.

You will see the following code snippet (your actual program may be inconsistent with my Code address, but it does not affect your understanding ):

Continue with tobject. Create (0x00407e27 ):

From the code, we can clearly see that the program calls the system-level function @ classcreate and then @ afterconstruction,

Let's see if we can find these two functions in system. Pas. Haha, found it!

However, the function names are _ classcreate and _ afterconstruction. Let's take a closer look at their implementation methods:

function _ClassCreate( AClass : TClass; Alloc : Boolean ) : TObject;asm  { -> EAX = pointer to VMT }  { <- EAX = pointer to instance }  PUSH EDX  PUSH ECX  PUSH EBX  TEST DL,DL  JL @@noAlloc  CALL DWORD PTR [EAX] + VMTOFFSET TObject.NewInstance@@noAlloc:  {$IFNDEF PC_MAPPED_EXCEPTIONS}  XOR EDX,EDX  LEA ECX,[ESP+16]  MOV EBX,FS:[EDX]  MOV [ECX].TExcFrame.next,EBX  MOV [ECX].TExcFrame.hEBP,EBP  MOV [ECX].TExcFrame.desc,offset @desc  MOV [ECX].TexcFrame.ConstructedObject,EAX { trick: remember copy to instance }  MOV FS:[EDX],ECX  {$ENDIF}  POP EBX  POP ECX  POP EDX  RET  {$IFNDEF PC_MAPPED_EXCEPTIONS}@desc:  JMP _HandleAnyException  { destroy the object }  MOV EAX,[ESP+8+9*4]  MOV EAX,[EAX].TExcFrame.ConstructedObject  TEST EAX,EAX  JE @@skip  MOV ECX,[EAX]  MOV DL,$81  PUSH EAX  CALL DWORD PTR [ECX] + VMTOFFSET TObject.Destroy  POP EAX  CALL _ClassDestroy@@skip:  { reraise the exception }  CALL _RaiseAgain  {$ENDIF}end;function _AfterConstruction( Instance : TObject ) : TObject;begin  Instance.AfterConstruction;  Result := Instance;end;

 

Unexpectedly, the implementation of function _ classcreate is completely embedded with assembly code, showing its importance.

The main purpose of code expression is to call virtual functions in the virtual method table (vitual method table (VMT) [1 ].

Newinstance to complete object instance creation and partial initialization.

In addition, set function _ afterconstruction to call the virtual function afterconstruction in VMT,

For more information about the implementation of _ newinstance and _ afterconstruction, see the relevant code.

Now we should have a rough flowchart in our mind. The code is summarized as follows [1]:

Call the create constructor of tobject, while the create constructor of tobject calls the classcreate process of the system.

The classcreate process of the system calls the newinstance virtual method of the tobject class.

The newinstance method of tobject is called to create the instance space of the object.

The newinstance method of the tobjec class will be based on the size of the object instance (instancesize) initialized by the compiler in the class information data ),

Call getmem to allocate memory for the object.

Call the tobject class initinstance method to initialize the allocated space.

The initinstance method first initializes the first four bytes of the object space as a pointer to the VMT of the object class, and then clears the remaining space.

At the end of the object instance creation, a virtual method afterconstruction is also called.

Finally, the address pointer of the object instance data is saved to the OBJ variable, so that the OBJ object is born.

To sum up, this process can be represented by the following code [3]:

Programmer call (Code-level call) Internal System Call (compiler-level call)

TObject.Create; => @ClassCreate;  => TObject.NewInstance; @AfterConstruction; => TObject.AfterConstruction; 

 

 

Tobject. Create calls system. _ classcreate and calls tobject. newinstance to call tobject. initinstance, and finally calls tobject. afterconstruction.

 

Let's look at how the constructor works when the inheritance mechanism is used. Let's look at the implementation of tderive. Create,

Set a breakpoint in line 1 of the source code: the debugging result shows the process difference from tobject. Create is only in Figure 2, and only the code corresponding to Figure 2 is pasted:

The code can be analyzed as follows:

Because the member functions newinstance and afterconstruction in tobject are inherited by tderive,

Therefore, der can be successfully created in heap like obj. In addition, the compiler only implements the tderive. Create part (from 0x407d38 to 0x00407d46 ),

That is, the real self-written constructor code is placed between the system-level call functions @ classcreate and @ afterconstruction.

In addition, it is implemented by inserting code (called the inline member function in C ++), which completes the initialization of Der object members.

Note: Do not overwrite the newinstance of tobject in the inheritance class unless you know what you are doing.

And reload the initinstance that it indirectly calls. These vital functions should be called by the system.

Let's continue to look at the implementation of tderive1's create inherited class of tderive. Where can I set a breakpoint?

Make a direct comparison with the section corresponding to figure 2 and Figure 3:

The Code shows that the compiler implements tderive1.create (from 0x407d73 to 0x00407d82)

Placed between the system-level call functions @ classcreate and @ afterconstruction,

The line of inherited create code calls tderive. Create,

However, you will find that the next call here is significantly different from the call shown in Figure 1. This line of code in Figure 4:


00407d79 33d2 XOR edX, EDX


This will cause the register DL = 0. Let's look at this line in Figure 1:


00407e20 b201 mov DL, $01


The register DL is 1, and the Register DL is used to indicate the alloc: Boolean parameter in system-level function _ classcreate.

(But you will find that there are no operations on alloc in the _ classcreate implementation code,

This is only because the compiler optimizes the code, or is an implicit parameter ),

It indicates whether system-level functions _ classcreate and _ afterconstruction need to be called during object creation,

That is, when constructing an instance of a derived class Object Based on the tobject class, the constructor is called for the first time, and the alloc parameter is set to true,

It indicates that you need to allocate memory space for the object instance and perform initialization and some post-creation work,

When the constructor of the parent class is called in the inherited constructor, this implicit parameter is set to false again,

In this way, the @ classcreate and @ afterconstruction functions will not be called again,

This means that you no longer need to allocate memory space for the object. You can imagine what kind of consequence will occur if you allocate memory space for the object again.

The conclusion is that no matter how long a class of the inheritance chain is created,

@ Classcreate and @ afterconstruction are called only once,

The constructor of the parent class is called cyclically only to initialize the object members declared in the parent class. This is exactly consistent with our design purpose!

Now let's look back at why the tobject. Create implementation code is empty. Isn't that strange.

As the base class of all classes, it does not have any member data to be initialized, so it does not need to be superfluous, waiting for the inherited class to reload its constructor.

You can try to comment out the line "inherited create" (that is, row 41st) in the Implementation part of tderive1.create in the source code,

Compile the program again and debug it again. You will find that the compiler does not automatically call the tderive constructor,

This is different from the implementation method in C ++. Delphi always constructs the derived class first, and constructs the base class only when the derived class calls the inherited constructor.

In C ++, the order is the opposite. The class is constructed from the ancestor class, and finally the derived class. [4]

As long as you follow the op rules, that is, when writing the constructor of the inherited class, do not forget to use the inherited reserved word to call the constructor of the parent class,

In this way, when you create a class with a long inheritance chain, you can ensure that create is the chain initialization from the parent class to the subclass.

Finally, let's simulate a system-level op constructor in the op language.

When the system encounters constructor reserved words or inherited create, the compiler will do the following for us:

function TSomething.Create(Alloc: Boolean): TSomething;begin  if Alloc then     Self := _ClassCreate(True);        // 真正的初始化代码  inherited Create(False); //如果有基类的构造器,别忘了加上这行  // ....  if Alloc then     _AfterConstruction;  result := Self;end; 

 

 

Tobject-object creation process

Http://www.xuebuyuan.com/1784386.html

The generation of a class instance requires three steps: Object Memory Allocation, memory initialization, and object execution framework setting.

The compiler first calls system. _ classcreate for Object Memory Allocation and memory initialization.

While system. _ classcreate calls the virtual method newinstance of the tobject class to create the instance space of the object,

The inheritance class usually does not need to overload tobject. newinstance, unless you use your own memory manager,

Therefore, tobject. newinstance is called by default.

The tobject. newinstance method initializes the object instance size (tobject. instancesize) according to the compiler's class information data ),

Call the system's default memorymanager. getmem process to allocate memory for this object in heap,

Call tobject. initinstance to initialize the allocated space.

The initinstance method first initializes the first four bytes of the object space as a pointer to the VMT of the object class,

Then, the remaining space is cleared. If an interface is also designed in the class, it also initializes the interface table ).

When the object instance is allocated in the memory and initialized, the execution framework is set.

The so-called setup execution framework is to execute the code you actually write in the create method.

The rule for setting the execution framework is to first set the framework of the base class, and then set the inheritance class, which is usually implemented using the inherited keyword.

After all the above work is done, the compiler also calls system. _ afterconstruction

This gives you the last chance to handle some transactions.

System. _ afterconstruction is implemented by calling the virtual method afterconstruction.

In tobject, afterconstruction is only a place holder,

You seldom need to reload this method, which is generally only used to be compatible with the c ++ builder object model.

Finally, the compiler returns the address pointer of the object instance data.

The object release service is actually the inverse process of object creation service. It can be considered that the object release service is to recycle the resources allocated during object creation.

When the compiler encounters the Destructor keyword, the encoding is usually as follows:

First, call system. _ beforedestruction, while system. _ beforedestruction then calls the virtual method beforedestruction,

In tobject, beforedestruction is just a place holder. You rarely need to overload this method,

This method is often used to be compatible with the c ++ builder object model.

After that, the compiler calls the code you actually write in destroy. If the class you are writing is a member of the inheritance chain,

Do not forget to call the destructor of the parent class through inherited to release the resources allocated by the parent class,

But the rule is that the resources of the current class are released first, and then the parent class is called, which is the opposite of setting the object execution framework in the object creation service.

After all the resources allocated to the current class and all the classes in the inheritance chain are released, the final execution is to release the object itself and some memory space occupied by special data types.

The compiler calls system. _ classdestroy to complete this task.

System. _ classdestroy then calls the virtual method freeinstance. The Inheritance class usually does not need to overload tobject. freeinstance,

Unless you use your memory manager, tobject. freeinstance is called by default.

Tobject. freeinstance and then call tobject. cleanupinstance to complete

Release of resources occupied by special data types such as string array, wide string array, variant, undefined type array, record, interface, and dynamic array [4],

Finally, tobject. freeinstance calls memorymanager. freemem to release the memory space occupied by the object itself.

Note that we usually do not directly call destroy to release objects,

Instead, call tobject. Free to check whether the object reference is nil before releasing the object.

 

 

Interestingly, the methods and functions used by the object release service and the object creation service correspond one by one. Is there a neat feeling?

System._ClassCreateSystem._ClassDestroySystem._AfterConstructionSystem._BeforeDestructionTObject.AfterConstruction(virtual)TObject.BeforeDestruction(virtual)TObject.NewInstance(virtual)TObject.FreeInstance(virtual)TObject.InitInstanceTObject.CleanupInstanceMemoryManager.GetMemMemoryManager.FreeMem

 

 

 

Research on constructor in Object Pascal Object Model

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.