Mega Search
23.2 Million


Sign Up

Make a donation  
Pointers, Delphi 7  
News Group: embarcadero.public.delphi.oodesign

I have a major software package used by veterinary clinics, and have never used pointers in it. 
 I would like to know is there is an advantage to using pointers, and if so, where?  A major part
of the app is invoicing.  Each time an invoice is opened I store the 
   Clients last name
   Clients first name
   Clients number
   Patient's (pet) name
   Patient's number
to variables and use them frequently as the invoice is built and posted.  Where in this process
can I (and should I) use pointers?  Also, as I read on pointers I see a lot of
  GetMem and
   FreeMem
stuff.  But I see in the Delphi help that the use of
   New and
   Dispose
is preferred.  I would appreciate any insight anyone could give me on this subject.

Thanks,
Jim Sawyer

Vote for best question.
Score: 0  # Vote:  0
Date Posted: 8-Jun-2014, at 2:12 PM EST
From: Jim Sawyer
 
Re: Pointers, Delphi 7  
News Group: embarcadero.public.delphi.oodesign
No specific problem.  I'm mostly trying to get a better handle on using pointers and when it is beneficial to do so.
I guess I'd better also brush up on when and how to use CREATE  and FREE.

Dawson!  I grew up in Dawson, TX.

> {quote:title=Robert Dawson wrote:}{quote}
>  wrote
> > I would like to know is there is an advantage to using pointers, and if 
> > so, where?
> 
> Depending on context, there could be, but what you're mentioning sounds more 
> like potential class structures than an occasion pointer use. Given a class, 
> instance creation would generally by done using Create and Free calls.
> 
> Is there a specific problem or problems you're trying to address?
> 
> bobD

Vote for best answer.
Score: 0  # Vote:  0
Date Posted: 8-Jun-2014, at 6:53 PM EST
From: Jim Sawyer
 
Re: Pointers, Delphi 7  
News Group: embarcadero.public.delphi.oodesign
How do I free the memory in the following example:

procedure TForm.btnTestClick( Sender );
type
   TLoggedClient = record
      CL: String;
      CF: String;
      CN: String;
   end;
var
   LoggedClient: TLoggedClient;
begin
   LoggedClient.CL := 'Samples';
   LoggedClient.CF := 'Junior';
   LoggedClient.CN := 'BR549';
   ShowMessage( LoggedClient.CF + ' ' + LoggedClient.CL + ', ' + LoggedClient.CN );

end;

How do I free the memory after showing the message?

Thanks,
Jim Sawyer




> {quote:title=Robert Dawson wrote:}{quote}
>  wrote
> > No specific problem.  I'm mostly trying to get a better handle on using
> > pointers and when it is beneficial to do so.
> 
> Serious use now would be high speed computational engines--complex 
> simulations, encryption, low level drivers--that sort of thing.
> 
> By contrast, anywhere where the system is interacting with a user or 
> peripheral (printer, screen, database, harddrive, etc.) the latency of those 
> operations is orders of magnitudes beyond what the difference between getmem 
> or new is going to be. Spped today, in general business computing, is driven 
> more by the domain model efficiency and algorithm choices than low-level 
> coding techniques.
> 
> > I guess I'd better also brush up on when and how to use CREATE  and FREE.
> 
> This is as good a place as any for getting started with classes.
> 
> > Dawson!  I grew up in Dawson, TX.
> 
> There's a Dawson, Alaska, too. No family history through either, though. 
> Have lived in Killeen and Kingsville, Tx.
> 
> bobD

Vote for best answer.
Score: 0  # Vote:  0
Date Posted: 9-Jun-2014, at 8:59 AM EST
From: Jim Sawyer
 
Re: Pointers, Delphi 7 [Edit]  
News Group: embarcadero.public.delphi.oodesign
I'm in the process of implementing the stuff you folks taught me below, during which I came across
a conumdrum that begs the following question:

I actually have multiple locations where the same TObject needs to be created.  Is there a way to
test to see of the TObject has been created and has not yet been FREEd?  This would make
life a lot easier.

Thanks,
Jim Sawyer



Thank you so much!  You have done a great job of clarifying this for me.  Now to
get proficient with it...

Jim Sawyer


> {quote:title=Wayne Niddery wrote:}{quote}
> "Jim Sawyer" wrote in message news:679647@forums.embarcadero.com...
> > How do I free the memory in the following example:
> >
> > procedure TForm.btnTestClick( Sender );
> > type
> >   TLoggedClient = record
> >      CL: String;
> >      CF: String;
> >      CN: String;
> >   end;
> > var
> >   LoggedClient: TLoggedClient;
> > begin
> >   LoggedClient.CL := 'Samples';
> >   LoggedClient.CF := 'Junior';
> >   LoggedClient.CN := 'BR549';
> >   ShowMessage( LoggedClient.CF + ' ' + LoggedClient.CL + ', ' + 
> > LoggedClient.CN );
> >
> > end;
> >
> > How do I free the memory after showing the message?
> 
> 
> Here you have used a *record* not a class. As a result, the LoggedClient 
> variable is allocated for you (on the stack) as a local variable when you 
> enter the procedure and deallocated automatically when you exit the 
> procedure. There is nothing for you to manually create or free.
> 
> If you define LoggedClient as a *Class* then you would need to both create 
> and free it and the proper code structure for it, in this example, would be:
> 
>     LoggedClient := TLoggedClient.Create;
>     try
>        LoggedClient.CL := 'Samples';
>        LoggedClient.CF := 'Junior';
>        LoggedClient.CN := 'BR549';
>        ShowMessage( LoggedClient.CF + ' ' + LoggedClient.CL + ', ' + 
> LoggedClient.CN );
>     finally
>         LoggedClient.Free;
>     end;
> 
> If your use of either records or classes is going to be limited to local 
> variables in a procedure like this then there is no real call for them, 
> continue to simply use variables like
>     firstname: string;
> etc.
> 
> However, as soon as you have a need for such information to persist over 
> more than one procedure or function, or you need to pass the information 
> around from one procedure to another or one class to another, then it pays 
> to define such records or classes.
> 
> If you only have a need for a single instance of such a structure, then a 
> record will do. If you need to allow for multiple instances then, while 
> records *can* still be used, classes are better and easier.
> 
> So for example, if you are only ever working with a single client at a time 
> in a form then this might do:
> 
> type
>    TLoggedClient = record
>       CL: String;
>       CF: String;
>       CN: String;
>    end;
> 
>     TForm1 = class
>     [...]
>     private
>         LoggedClient: TLoggedClient;
>     end;
> 
> Then in one of your methods of this form you will assign the values to that 
> record, and all other methods and events of the form can then see those 
> values, and if you need to pass it to, say, another form, you only need to 
> pass the LoggedClient variable, not the separate CL,CF, and CN fields.
> 
> Note also that, whether a record or class, you can add methods. E.g.
> 
>    TLoggedClient = record
>       CL: String;
>       CF: String;
>       CN: String;
>       function FullName: string;
>    end;
> 
> implementation
> 
> function TLoggedClient.FullName: string;
> begin
>     Result := CL + ', ' + CF;
> end;
> 
> ...
>     ShowMessage(LoggedClient.FullName);
> 
> -- 
> Wayne Niddery
> "You know what they call alternative medicine that has been proven to work? 
> Medicine." - Tim Minchin

Edited by: Jim Sawyer on Jun 9, 2014 11:44 AM

Vote for best answer.
Score: 0  # Vote:  0
Date Posted: 9-Jun-2014, at 2:49 PM EST
From: Jim Sawyer
 
Re: Pointers, Delphi 7 [Edit]  
News Group: embarcadero.public.delphi.oodesign
I did not use very good terminology in my last question.  Let me try again.

I have two objects.  The first is TLoggedClient and the other is TLoggedPatient.
For the invoicing process, I create LCli from TLoggedClient in which I store the
client's information for use during the tangled tenacles of the invoicing process,
and I also create LPat from TLoggedPatient in which I store the patient's (pet's)
information.  It is possible (necessary) that I initiate the invoicing process from
multiple locations (logging for the client/patient grids, from the appointment
calendar, when automatically converting an estimate to an invoice, etc)  So when
I create LCli and LPat, I would like to test to determine that they haven't already 
been created.

After the post yesterday, I started using 

if not Assigned( LCli ) then
   LCli := TLoggedClient.Create;
if not Assigned( LPat ) then
   LPat := TLoggedPatient.Create;

Preliminary testing indicates that those bits of code may suffice.  

Let me say that I have been marketing this system in Delphi since
1995 and have been a pretty good programming rut and have never
had programming training.  As I learned this concept yesterday (at 
77 years old I might add) I felt like a whole new tool package became
available.  Much like when I discovered DevExpress components
and LayOut package.  Thanks for you guys help in that process.


> {quote:title=Robert Dawson wrote:}{quote}
>  wrote
> >
> > I actually have multiple locations where the same TObject needs to be 
> > created.
> 
> I have the same question as John---exactly what does "the same TObject" 
> mean?
> 
> It sounds like you're thinking in terms of records, where generally one 
> record declaration means one instance. You have to think in terms of class 
> as a declaration without an instance, and specific instance, since TClient 
> is a structure representative of all clients but doesn't accully get 
> instantiated until you create it, whereas a class instance can exist even 
> after the variable pointing to it goes out of scope:
> 
> var.
>   myClient  TClient;
> begin
>   myClient := TClient.Create('Joe', 'Green', 1);
> end;
> //memory leak--Joe Green still exists on the heap, but it's no longer 
> available
> 
> Specific questions:
> 1. Is it possible to have Joe Green and Joe Stream both in memory at the 
> same time?
> 2. If not, what happens when you're working with Joe Green, but need to 
> change to Joe Stream?
> 3. If so (more than one client instance can exist), then how does the user 
> code know which instance to create or request?
> 
> The classic way to handle the situation where access to a specific instance 
> has to be obtained from multiple locations in the program would be a 
> factory: the factory tracks the status of the instance(s), and all users ask 
> the factory for access rather than creating the instance they need 
> themselves.
> 
> bobD

Vote for best answer.
Score: 0  # Vote:  0
Date Posted: 10-Jun-2014, at 6:39 AM EST
From: Jim Sawyer
 
Re: Pointers, Delphi 7 [Edit]  
News Group: embarcadero.public.delphi.oodesign
> {quote:title=Jim Sawyer wrote:}{quote}
>> 
> if not Assigned( LCli ) then
>    LCli := TLoggedClient.Create;
> if not Assigned( LPat ) then
>    LPat := TLoggedPatient.Create;

What's the scope of the LCli and LPat variables? (IOW, where are they declared?) Are they single variables, visible globally?

bobD

Vote for best answer.
Score: 0  # Vote:  0
Date Posted: 11-Jun-2014, at 7:44 AM EST
From: Robert Dawson
 
Re: Pointers, Delphi 7 [Edit]  
News Group: embarcadero.public.delphi.oodesign
LCli and LPat are created when an invoice is opened and remain until the invoice is either posted or closed for work on 
a different invoice.  LCli is constant for an invoice since there is only one client (owner), but since the invoice can involve
more than one of the owner's pets, LPat changes to represent the patient on which attention is being focused at any 
given time.  Services, products, and diagnosises will be given from different pick-lists (window with a grid), so as one 
pet gets attention LPat points to that patient.  LCli and LPat continue in action through invoice posting which includes
updating medical records, creating an invoice record, printing invoice, vaccination sheet, report card, check-out sheet,
and finally, when this is completed, the memory is freed.  Of course, several dozen invoices are switched between
frequently, so they are freed and re-created for another client/patient often without completing the invoice.  But a workstation
will maintain only one LCli and one LPat (although it's value may change frequently in the multiple-patient case) at a time.

Define "visible globally".  The values are visible only to one operator on one workstation.  If two workstations were 
maintaining the same invoice at the same time, they would have similar values, but would be created and freed 
independent of each other.

Jim Sawyer



> {quote:title=Robert Dawson wrote:}{quote}
> > {quote:title=Jim Sawyer wrote:}{quote}
> >> 
> > if not Assigned( LCli ) then
> >    LCli := TLoggedClient.Create;
> > if not Assigned( LPat ) then
> >    LPat := TLoggedPatient.Create;
> 
> What's the scope of the LCli and LPat variables? (IOW, where are they declared?) Are they single variables, visible globally?
> 
> bobD

Vote for best answer.
Score: 0  # Vote:  0
Date Posted: 11-Jun-2014, at 8:10 AM EST
From: Jim Sawyer
 
Re: Pointers, Delphi 7 [Edit] [Edit]  
News Group: embarcadero.public.delphi.oodesign
I Define the types in a wrapper visible from anywhere in the program.  I generate
an instance where LCli's 3 variables contain the client's last name, the client's firstname,
and the client number.  LPat's 2 variables contain the patient's name and number.
I can generate the instances from
1.  The client/patient screen (master/slave grids where patient is slave)
2.  The patient screen (master/slave grids where the client is slave)
3.  The appointment calendar when an appointment shows up
4.  The boarding map when the patient is ready to go home
5.  From the client patient screen for the generation of an estimate to keep on file
6.  From the estimate screen when the estimate is to automatically convert to an invoice.

It's sometimes a bit spaghettiish.

Thanks
Jim Sawyer


> {quote:title=Robert Dawson wrote:}{quote}
>  wrote in message news:680054@forums.embarcadero.com...
> > LCli and LPat are created when an invoice is opened and remain until the 
> > invoice is
> > either posted or closed for work on a different invoice.
> 
> Is there anywhere other than within the context of an invoice where an LCli 
> is created?
> After the line
>    LCli := TLoggedClient.Create;
>  ---how does the LCli structure obtain its data? (FName, LName, etc)
>  ---does that data ever change within the context of the invoicing?
> ---does the LCli ever _do_ anything within the context of the invoicing 
> (IOW, have methods/operations called on it, other than to read its 
> data/properties? (sounds like no)
> 
> [...]
> >  a workstation will maintain only one LCli and one LPat (although it's 
> > value may change
> > frequently in the multiple-patient case) at a time.
> 
>  Generally for an OO system, there's going to be a 
> 1::1 relationship between a class instance and the real world object that 
> that instance represents. For example, IIRC your system has a Patient ID 
> field; that's presumably unique within the scope of all patients for the 
> installation. You might then create an LPat patient instance by that 
> identifier
>    patient := TLoggedPatient.Create(;
> and on the instance itself the ID property would be readonly--not possible 
> to change.
> 
> Besides the invoicing function, in what other major systems would these 
> classes play a role?
> Does each have it's own declaration of an LCli and LPat variable?
> Does each have it's own code for filling their values?
> 
> > Define "visible globally".
> 
> For example, there's a unit for the Client class looking something like
> 
> unit LoggedClient
> 
> interface
> 
> type
>   TLoggedClient = class(TObject)
>     //
>   end;
> 
> var
>   client : TLoggedClient
> 
> implementation
> // etc.
> 
> This is essentially the default declaration pattern for Delphi forms.
> 
> bobD

Edited by: Jim Sawyer on Jun 13, 2014 5:43 AM

Vote for best answer.
Score: 0  # Vote:  0
Date Posted: 13-Jun-2014, at 8:43 AM EST
From: Jim Sawyer
 
Re: Pointers, Delphi 7 [Edit] [Edit]  
News Group: embarcadero.public.delphi.oodesign
I'm trying to wrap my noggin around this new (potential) knowledge.  I assume
you are saying that the initial action of "logging a patient" would CREATE then
instance which would be FREEd at the bottom of that action.  That would provide
some reliable variables for filling up with separate code.  I also assume there is
no problem for me changing the value of the PAT (patient) variable during the 
processing of a multi-patient invoice.

I'm also trying to figure out where I could make use of a function included in 
the type.  Would it be possible to use VAR parameters in such a function (or 
maybe a procedure) so it could maintain multiple variables?  I hope this 
question makes sense!

Thanks
Jim Sawyer



> {quote:title=Robert Dawson wrote:}{quote}
>  wrote
> > I can generate the instances from
> > 1.  The client/patient screen (master/slave grids where patient is slave)
> > 2.  The patient screen (master/slave grids where the client is slave)
> > 3.  The appointment calendar when an appointment shows up
> > 4.  The boarding map when the patient is ready to go home
> > 5.  From the client patient screen for the generation of an estimate to 
> > keep on file
> > 6.  From the estimate screen when the estimate is to automatically convert 
> > to an invoice.
> 
> Does each calling location fill the fields? If so, that's an area you could 
> simplify by moving the responsibility for setting up the Cli/Pat objects 
> into their own subsystem. The front end functions/systems request the 
> objects, but they don't have any knowledge of their internal set-up process 
> or data source. They're just guaranteed that, when they need instance 
> , they get it, and that's done correctly because there's only 
> one place in the program that that actually happens.
> 
> bobD

Vote for best answer.
Score: 0  # Vote:  0
Date Posted: 13-Jun-2014, at 2:03 PM EST
From: Jim Sawyer
 
Re: Pointers, Delphi 7 [Edit] [Edit]  
News Group: embarcadero.public.delphi.oodesign
I have defined in a wrapper

type
  str24 = string[24];
  str16 = string[16];
  str8 = string[8];

TLogged = class(TObject)
CF: Str24;
CL: Str24;
CN: Str8;
Pat: Str16;
Num: Str8;
end;

TFactory = class( TObject )
CP: Boolean;
end;

var
Factory: TFactory;
Lcp: TLogged;

As the program is opened I have

Factory := TFactory.Create

and as it closes I

Factory.Free

so it is available for use all during the execution of the program.

Now in the program where ever I need to start using the Lcp I do something like

if not Factory.CP then
Factory.CP := true;
Lcp := TLogged.Create;
end
Lcp.CL := ClientTSet.FieldAsString('Lastname');
Lcp:CF := ClientTSet.FieldAsString('Firstname');
Lcp.CN := ClientTSet.FieldAsString('Clino');
Lcp.Pat := PatientTSet.FieldAsString('Name');
Lcp.Num := PatientTSet.FieldAsString('Patno');
// going into the wilderness where these values will be used
OpenForm( TfrmInvoice, frmInvoice );   // this is just a procedure Ive defined for opening a form
//then when finally when this process is complete and exited
if Factory.CP then
begin
Factory.XP := false;
Lcp.Free;
end;

Thanks,
Jim Sawyer



> {quote:title=Robert Dawson wrote:}{quote}
>  wrote
> > you are saying that the initial action of "logging a patient" would CREATE 
> > then
> > instance which would be FREEd at the bottom of that action.
> 
> An object instance created in a procedure needn't be freed in that 
> procedure. Can be, but not a rule.
> 
> Let's start w/ a simple case as you currently do it. Something like this I 
> assume:
> 
> var
>   client : TLoogedClientRecord;
> begin
>   client.FirstName := ?? Where do client records get their values from?
>   // business code for doing something with client...
> 
> I'm quessing that you're loading from a dataset, since you mentioned using 
> the DevExpress grid. That right? Show me how clients are currently loaded. 
> I'll show you some options from there.
> 
> bobD

Vote for best answer.
Score: 0  # Vote:  0
Date Posted: 14-Jun-2014, at 8:08 AM EST
From: Jim Sawyer