Go Delphi2009 first Experience-language-smart pointers (smart Pointer) implementation

Source: Internet
Author: User

Quick Navigation

I. Review of History
Ii. Introduction to Smart pointers
Third, the interface in Delphi
Iv. implementation of intelligent pointers in Delphi
Five, interface + generic = strong type of smart pointer!
Vi. Smart Pointers and collections
VII. Matters of attention
Viii. Summary

This essay all source code package download

I. Review of History

In C + +, objects can be created in a stack or created in a heap. Such as:

Class Ctestclass
{
Public
Ctestclass ()
{
printf ("Create");
}

void Doprint () {}

~ctestclass ()
{
printf ("Destroy");
}
}

The following code creates a stack object

Ctestclass test;
Test. Doprint ();

The Stack object life cycle is managed by the background. When the method ends, the Stack object pops out of the stack, and the compiler automatically destroys the object that the stack pops up.

The following code creates a heap object

ctestclass* test = new Ctestclass ();
Test->doprint ();

Heap objects are stored in the heap, the heap object life cycle is not managed by the background, and the programmer must manually dispose of the heap objects themselves, otherwise it will cause a memory leak:

Delete test;
Test = NULL;

The Pascal language supports object-oriented from OOP Pascal, which means that OOP Pascal supports the creation of objects. OOP Pascal, like C + +, can also create stack objects and heap objects, respectively:

The definition and creation of our most common OOP Pascal heap objects:

Code
Type
Theapobject = class//Note the declaration here, the class table name is the heap object
Public
Constructor Create; Virtual
destructor Destroy; Override
Procedure Doprint;
End

Var
Heapobj:theapobject;

{Tstackobject}

Constructor Theapobject.create;
Begin
Writeln (' Create ');
End

destructor Theapobject.destroy;
Begin
Writeln (' Destroy ');
End

Procedure Theapobject.doprint;
Begin
Writeln (' Doprint ');
End

Begin
Heapobj: = theapobject.create;
Heapobj.doprint;
Freeandnil (Heapobj);

READLN;
End.

Operation Result:

OOP Pascal also has stack objects, the definition and creation of stack objects:

Type
Tstackobject = object//Note the declaration here that object is a reserved word indicating a stack object
Public
Constructor Create;
destructor Destroy;
Procedure Doprint;
End

Var
Stackobj:tstackobject;

{Tstackobject}

Constructor Tstackobject.create;
Begin
Writeln (' Create ');
End

destructor Tstackobject.destroy;
Begin
Writeln (' Destroy ');
End

Procedure Tstackobject.doprint;
Begin
Writeln (' Doprint ');
End

Begin
Note that the code here does not require the use of tstackobject.create
Stackobj.doprint;

READLN;
End.

Operation Result:

From the results we can see, unlike C + +, OOP Pascal so-called stack object construction and destruction, not controlled by the constructor method and destructor method, we can not capture the OOP Pascal stack object construction and destruction.

Ii. Introduction to Smart pointers

After analysis, we know that the declaration period of the Stack object is managed by the background, the stack object is constructed at the time of declaration, when the method exits or when the class is destroyed (when the Stack object is a member variable of the Class), the life cycle of the stack object will end, and the background will automatically invoke their destructor and free up the stack space.
And the heap object must be manually released by the programmer, if a method only one or two heap objects we can cope with, but when the heap objects are very many, and the heap objects are usually passed through multiple methods, assignment, passed to the end, it is very easy to forget the delete, causing memory leaks.

Can you let the backstage also automatically manage the release of the heap objects? Predecessors think of a way to let a stack object contains a reference to a heap object, when the Stack object is automatically released in the background, will call the Stack object destructor, so, in the Stack object destructor write down the Delete heap object pointer statement. In this way, the background of the indirect management of the heap objects, the above is the STL's smart pointer auto_ptr processing method.

Third, the interface in Delphi

From the introduction of smart pointers, we can see that to use smart pointers, we have to capture the constructor of the stack object, pass the pointer of the heap object to the Stack object, save the pointer to the heap object by the Stack object, and must capture the destructor of the Stack object. In the destructor of the stack object, the pointer to the heap object passed to the constructor is delete. In C + + it is easy to do this, but through the above analysis, we can not be the Delphi stack object construction and destruction of the capture.
We can think of a different angle, not necessarily a stack object, as long as there is a thing in Delphi, as long as its scope, it can automatically destruction!

Delphi's interface can indirectly meet our needs, see the following example:

code
Program TestInterface;

{$APPTYPE CONSOLE}

uses
Sysutils;

Type
Itestinterface = Interface
[' {ed2517d5-fb77-4dd6-bc89-df9182b335ae} ']
procedure doprint;
End ;

Ttestinterface = Class (Tinterfacedobject, Itestinterface)
Public
Constructor Create, virtual;
destructor Destroy; Override
Procedure Doprint;
End;

{ttestinterface}

Constructor ttestinterface.create;
Begin
Writeln (' Create ');
End

destructor Ttestinterface.destroy;
Begin
Writeln (' Destroy ');

inherited;
End;

Procedure Ttestinterface.doprint;
Begin
Writeln (' Doprint ');
End

Procedure dotest;
var
testinter:itestinterface;//1*
begin
Testinter: = ttestinterface.create;
Testinter.doprint;
End;

begin
Dotest;
READLN;
End.

As you can see, the object that Testinter points to is not released in the code, and the object is freed from the background. If the 1* is changed to Testinter:ttestinterface, the result is as follows, and we will see that if you do not declare an interface, Delphi does not automatically dispose of the object, even if you create the same object.

Here, we take advantage of the automatic management of the interface, which maintains a reference count itself, and when the reference count is 0 o'clock the interface itself calls the destructor. Some of the concepts of Delphi interface and why the background will automatically release the interface, you can refer to the following two articles, here do not make redundant narrative.

1, Delphi's interface mechanism shallow probing http://www.d99net.net/article.asp?id=206
2, talking about reference counting http://www.moon-soft.com/doc/13056.htm

Iv. implementation of intelligent pointers in Delphi

With the above experience, we can implement our smart pointers!

First, we will create an object that inherits from Tinterfacedobject, passing in the constructor a reference to the heap object to be managed, and freeandnil the reference to the heap object in the destructor. The code is as follows:

Unit classicalautoptr;

Interface

Uses
Sysutils;

Type
Tclassicalautoptr = Class (Tinterfacedobject)
Private
Fobj:tobject;
Public
Constructor Create (Aobj:tobject); Virtual
destructor Destroy; Override
class function New (aobj:tobject): IInterface;
End

Implementation

{TCLASSICALAUTOPTR}

Constructor Tclassicalautoptr.create (Aobj:tobject);
Begin
Fobj: = Aobj;
End

destructor Tclassicalautoptr.destroy;
Begin
The smart pointer is destroyed when the method exits and destroys the managed heap object
Freeandnil (Fobj);
inherited;
End

class function Tclassicalautoptr.new (aobj:tobject): IInterface;
Begin
External must use this method to create a smart pointer
Because this method is exposed to an external interface
The destructor of the interface is automatically called when the interface is encountered in the background
Result: = Tclassicalautoptr.create (aobj);
End

End.

Then we write a console program to do the experiment:

Program Testclassicalautoptr;

{$APPTYPE CONSOLE}

Uses
Sysutils,
Classicalautoptr in ' Classicalautoptr.pas ';

Type
Ttestclass = Class
Public
Constructor Create; Virtual
destructor Destroy; Override
Procedure Doprint;
End

{Ttestclass}

Constructor Ttestclass.create;
Begin
Writeln (' Create ');
End

destructor Ttestclass.destroy;
Begin
Writeln (' Destroy ');
inherited;
End

Procedure Ttestclass.doprint;
Begin
Writeln (' Doprint ');
End

Procedure Dotest;
Var
Tt:ttestclass;
Begin
Create a Heap object first
TT: = ttestclass.create;

Creates a smart pointer and passes a reference to the heap object into a smart pointer, which manages the heap object by a smart pointer
Tclassicalautoptr.new (TT); *
Tt. Doprint;
End

Begin
Dotest;
READLN;
End.

The code execution results are as follows:

If we replace the code
Tclassicalautoptr.create (TT);

The execution result will not see the destroy, and the destructor is not called. Because a interface is returned by Tclassicalautoptr.new, and Tclassicalautoptr.create returns an object.

So, we're done with a simple smart pointer.

V, interface + generic = strong type smart pointer

D2009 introduced generics, and we've changed the program a little bit to support strong-type smart pointers!

For an analysis of D2009 support for generics, see my other two essays:
Http://www.cnblogs.com/felixYeou/archive/2008/08/22/1273989.html
Http://www.cnblogs.com/felixYeou/archive/2008/08/22/1274202.html

We use STL's auto_ptr as a reference, and if our smart pointers look "elegant", we must also implement the following methods:
1. Get: Returns the object that the smart pointer points to
2, Release: Free intelligent refers to the management of the heap object, the smart pointer is automatically released, do not release the heap object
3. Reset: Point the smart pointer to other heap objects while releasing the previously pointed heap object

For auto_ptr some operator overloads, this is not taken into account, because Delphi2009 does not yet have operator overloads that support classes.
Words not much to say, directly on the code:

Smart Pointer Class code:

Unit autoptr;

Interface

Uses
Sysutils;

Type
Iautoptr<t:class> = Interface (iinterface)
[' {bd098fdb-728d-4cac-ae40-b12b8b556ad3} ']
function Get:t;
function Release:t;
Procedure Reset (aobj:t);
End

Tautoptr<t:class> = Class (Tinterfacedobject, Iautoptr<t>)
Private
Fobj:t;
Public
class function New (aobj:t): iautoptr<t>;
Constructor Create (aobj:t); Virtual
destructor Destroy; Override
function Get:t;
function Release:t;
Procedure Reset (aobj:t);
End

Implementation

{tautoptr<t>}

Constructor Tautoptr<t>. Create (aobj:t);
Begin
Fobj: = Aobj;
End

class function Tautoptr<t>. New (aobj:t): iautoptr<t>;
Begin
Result: = Tautoptr<t>. Create (aobj) as iautoptr<t>; Note: If you do not add as Iautoptr<t&gt, the program will error when running, the first time I did not add as Iautoptr<t> program run everything normal, to the back of the die, do not know why
End

function Tautoptr<t>. Release:t;
Begin
Result: = Fobj;
Fobj: = nil;
End

Procedure Tautoptr<t>. Reset (aobj:t);
Begin
If Aobj <> Fobj Then
Begin
Freeandnil (Fobj);
Fobj: = Aobj;
End
End

destructor Tautoptr<t>. Destroy;
Begin
If Fobj <> Nil Then
Begin
Freeandnil (Fobj);
End

inherited;
End

function Tautoptr<t>. Get:t;
Begin
Result: = Fobj;
End

End.

Test code:

Program Testautoptr;

{$APPTYPE CONSOLE}

Uses
Sysutils,
Autoptr in ' Autoptr.pas ';

Type
Ttestclass = Class
Private
Fint:integer;
Public
Constructor Create (Aint:integer); Virtual
destructor Destroy; Override
Procedure Doprintint;
End

{Ttestclass}

Constructor Ttestclass.create (Aint:integer);
Begin
Fint: = aInt;
Writeln (' Create ');
End

destructor Ttestclass.destroy;
Begin
Writeln (' Destroy ');

inherited;
End

Procedure Ttestclass.doprintint;
Begin
Writeln (Fint);
End

Procedure Dotestautoptr;
Var
ap:iautoptr<ttestclass>;
Begin
Both create and new are available here, because the AP object is an interface
AP: = Tautoptr<ttestclass>. Create (Ttestclass.create (10));
Ap. Get.doprintint; * *
End

Begin
Dotestautoptr;
READLN;
End.

The test results are:

However, we changed the code of the
Ap. Release.doprintint, the output result is

Because the release method has informed the smart pointer not to manage the heap object.

At the same time, we can also write the Dotestautoptr method in this way, perhaps creating a Ttestclass object is more graceful:

Procedure Dotestautoptr;
Var
Tt:ttestclass;
Begin
Note that you need to use the new
TT: = Tautoptr<ttestclass>. New (Ttestclass.create (10)). Get;

Tt. Doprintint;

No need to use TT. Free;

EndVi. Smart Pointers and collections

If we declare a global variable:

Var
gap:iautoptr<ttestclass>;

and start by changing the code from the Dotestautoptr method:

Procedure Dotestautoptr;
Var
Tt:ttestclass;
ap:iautoptr<ttestclass>;
Begin
AP: = Tautoptr<ttestclass>. New (Ttestclass.create (10));
TT: = AP. Get;
Tt. Doprintint;
GAp: = AP;
End

Begin
Dotestautoptr;
Writeln (' Exit dotestautoptr ');

Writeln (' gAp nil ');
GAp: = nil; The

READLN;
End.

The results are as follows:

We can see that when the Dotestautoptr method is finished, the heap object TT inside the method is not destroyed, indicating that the smart pointer AP is not destroyed.
Because in the last line of the Dotestautoptr method, the AP interface variable is assigned to the global variable gap, when the interface reference count +1, the method exits, the AP variable is destroyed, the interface reference count-1, but Gap still refers to the object, so the reference count is not 0. When running to the 4th * step, Force the gap to point to the empty address, the object's reference count-1, 0, this time the background automatically call the object's destructor destroy (which is a bit like Java or. NET garbage collection mechanism). Therefore, we use smart pointers, we can rest assured that the creation of the reference, without having to pipe when the destruction, completely by the background to help us achieve.

Let's change the test procedure to combine the smart pointer with the set test:

Code
Program Testautoptrlist;

{$APPTYPE CONSOLE}

Uses
Sysutils,
Generics.collections,
Autoptr in ' Autoptr.pas ';

Type
Ttestclass = Class
Private
Fint:integer;
Public
Constructor Create (Aint:integer); Virtual
destructor Destroy; Override
Procedure Doprintint;
End

Var
glist:tlist<iautoptr<ttestclass>>;
gap:iautoptr<ttestclass>;

{Ttestclass}

Constructor Ttestclass.create (Aint:integer);
Begin
Fint: = aInt;
Writeln (' Create ');
End

destructor Ttestclass.destroy;
Begin
Writeln (' Destroy ');

inherited;
End

Procedure Ttestclass.doprintint;
Begin
Writeln (Fint);
End

Procedure Dotestautoptr;
Var
ap:iautoptr<ttestclass>;
N:integer;
Begin
GList: = Tlist<iautoptr<ttestclass>>. Create;
For n: = 0 to 2 do
Begin
AP: = Tautoptr<ttestclass>. New (Ttestclass.create (10));
Ap. Get.doprintint;

Glist.add (AP);
AP: = nil;
End

Writeln (' Save an autoptr ');
GAp: = glist[1];

Writeln (' gList Destroy ');
Glist.free;

Writeln (' Set saved autoptr = Nil ');
GAp: = nil;
End

Begin
Dotestautoptr;

READLN;
End.

Test results:

VII. Matters of attention

1. Circular references between smart pointers and heap objects

If we make the following modifications to the Ttestclass class, let the heap object have a reference to its smart pointer:

Ttestclass = Class
Private
Fint:integer;
fap:iautoptr<ttestclass>;
Public
Constructor Create (Aint:integer); overload; Virtual
destructor Destroy; Override
Procedure Doprintint;
Property ap:iautoptr<ttestclass> read FAP write FAP;
End

At the same time, the test method is modified as follows:

Procedure Dotestautoptr;
Var
Tt:ttestclass;
ap:iautoptr<ttestclass>;
Begin
AP: = Tautoptr<ttestclass>. New (Ttestclass.create (10));
TT: = AP. Get;
Tt. AP: = AP; 5*
Tt. Doprintint;
End

At this point, we get a very dubious result:

The smart pointer is not automatically released!

From the above analysis and the previous code we can see that the interface reference count is 0, the interface will be automatically released, we want to ensure that the interface can be smoothly released, we must ensure that the interface reference count is 0.

From the 5th * Point Code we can see that TT. AP: = AP, which makes a circular reference between the smart pointer and the heap object, resulting in a reference count of +1 to 2 for the interface AP. Finally, when the method exits, although the reference occupied by the AP has been freed, reference-1, but because the heap object TT does not release itself, so the heap object TT. The reference used by the AP is not disposed, and when the method exits, the interface has a reference number of 1 and the interface is not automatically freed.

2. What to use when using the Release method

First we add use:Generics.Collections to the test unit, and then modify the Ttestclass class as follows:

Type
Ttestclass = Class
Private
flist:tlist<integer>;
Public
Constructor Create (Aint:integer); overload; Virtual
destructor Destroy; Override
Procedure Doprintint;
End

{Ttestclass}

Constructor Ttestclass.create (Aint:integer);
Begin
Inherited Create;

Flist: = Tlist<integer>. Create;
Flist.add (AINT);
Writeln (' Create ');
End


destructor Ttestclass.destroy;
Begin
Writeln (' Destroy ');
Freeandnil (flist);

inherited;
End

Procedure Ttestclass.doprintint;
Begin
Writeln (Flist[0]);
End

At this point, the member variable is no longer a value type, but a reference type.

The code to begin with the Dotestautoptr method is modified as follows:

Procedure Dotestautoptr;
Begin
GTt: = Tautoptr<ttestclass>. New (Ttestclass.create (10)). Get; 6*
End

Begin
Dotestautoptr;
Gtt.doprintint;

READLN;
End.

At this point, we create a smart pointer inside the Dotestautoptr method, pass the heap object pointed to by the smart pointer to the global variable, and then call the global variable's Doprintint method after the Dotestautoptr method execution finishes. Operation Result:

The run failed because the Dotestautoptr method exited after the Tautoptr<ttestclass>. The New (Ttestclass.create (10)) statement creates an interface reference count of 0, at which point it calls Ttestclass's Destroy method to destroy the flist. At this point, we call the Doprintint method to get the first element of Flist, but the flist itself has been destroyed, causing the error to occur.

We will replace the 6th * line with the following:

GTt: = Tautoptr<ttestclass>. New (Ttestclass.create (10)). Release;

Operation Result:

The error does not occur because the release method has notified that the smart pointer heap object is not managed by the smart pointer, so the Ttestclass destructor is not called when the tautoptr<ttestclass> is destroyed, and flist is preserved.

Here we can see that because the heap object is no longer managed by the smart pointer, we have to manually release it Freeandnil (gTt), otherwise it will produce the result: a memory leak.

Viii. Summary

Start implementing a Stack object I've considered that the record using Record,delphi is very similar to a class, saved in a stack, supports methods, properties, and constructors with parameters, but does not support destructors, so there is no way to implement our smart pointers. Delphi version of the smart pointer early in the Cnpack discussion area has been proposed by predecessors (http://bbs.cnpack.org/viewthread.php?tid=1399), but the use of inconvenient to make this style is not popular. Since D2009 supports generics, many of the previously cumbersome features can now be implemented very simply, such as the combination of smart pointers and generic collections. However, the use of smart pointers in Delphi is a little bit of performance loss, the current speed of the computer today, this loss has become insignificant.

This essay all source code package download: Http://files.cnblogs.com/felixYeou/auto_ptr_code.rar

Go Delphi2009 first Experience-language-smart pointers (smart Pointer) implementation

Related Article

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.