|
Delphi's Object Reference ModelIn some OOP languages, declaring a variable of a class type creates an instance of that class. Delphi, instead, is based on an object reference model. The idea is that a variable of a class type, such as the TheDay variable in the preceding ViewDate example, does not hold the value of the object. Rather, it contains a reference, or a pointer, to indicate the memory location where the object has been stored. You can see this structure depicted in Figure 2.4. Figure 2.4: A representation of the structure of an object in memory, with a variable referring to it The only problem with this approach is that when you declare a variable, you don't create an object in memory (which is inconsistent with all other variables, confusing new users of Delphi); you only reserve the memory location for a reference to an object. Object instances must be created manually, at least for the objects of the classes you define. Instances of the components you place on a form are built automatically by the Delphi library. You've seen how to create an instance of an object by applying a constructor to its class. Once you have created an object and you've finished using it, you need to dispose of it (to avoid filling up memory you don't need any more, which causes what is known as a memory leak). This can be accomplished by calling the Free method. As long as you create objects when you need them and free them when you're finished with them, the object reference model works without a glitch. The object reference model has many consequences on assigning object and on managing memory, as you'll see in the next two sections. Assigning ObjectsIf a variable holding an object only contains a reference to the object in memory, what happens if you copy the value of that variable? Suppose you write the BtnTodayClick method of the ViewDate example in the following way: procedure TDateForm.BtnTodayClick(Sender: TObject); var NewDay: TDate; begin NewDay := TDate.Create; TheDay := NewDay; LabelDate.Caption := TheDay.GetText; end; This code copies the memory address of the NewDay object to the TheDay variable (as shown in Figure 2.5); it doesn't copy the data of one object into the other. In this particular circumstance, this is not a very good approach—you keep allocating memory for a new object every time the button is clicked, but you never release the memory of the object the TheDay variable was previously pointing to. Figure 2.5: A representation of the operation of assigning an object reference to another object. This is different from copying the actual content of an object to another. This specific issue can be solved by freeing the old object, as in the following code (which is also simplified, without the use of an explicit variable for the newly created object): procedure TDateForm.BtnTodayClick(Sender: TObject); begin TheDay.Free; TheDay := TDate.Create; The important thing to keep in mind is that, when you assign an object to another object, Delphi copies the reference to the object in memory to the new object reference. You should not consider this a negative: In many cases, being able to define a variable referring to an existing object can be a plus. For example, you can store the object returned by accessing a property and use it in subsequent statements, as this code snippet indicates: var ADay: TDate; begin ADay := UserInformation.GetBirthDate; // use a ADay The same thing happens if you pass an object as a parameter to a function: You don't create a new object, but you refer to the same one in two different places in the code. For example, by writing this procedure and calling it as follows, you'll modify the Caption property of the Button1 object, not of a copy of its data in memory (which would be totally useless): procedure CaptionPlus (Button: TButton); begin Button.Caption := Button.Caption + '+'; end; // call... CaptionPlus (Button1) This means that the object is being passed by reference without the use of the var keyword and without any other obvious indication of the pass-by-reference semantic, which also confuses newcomers. What if you really want to change the data inside an existing object, so that it matches the data of another object? You have to copy each field of the object, which is possible only if they are all public, or provide a specific method to copy the internal data. Some classes of the VCL have an Assign method, which performs this copy operation. To be more precise, most of the VCL classes that inherit from TPersistent, but do not inherit from TComponent, have the Assign method. Other TComponent-derived classes have this method but raise an exception when it is called. In the DateCopy example, I've added an Assign method to the TDate class and called it from the Today button, with the following code: procedure TDate.Assign (Source: TDate); begin fDate := Source.fDate; end; procedure TDateForm.BtnTodayClick(Sender: TObject); var NewDay: TDate; begin NewDay := TDate.Create; TheDay.Assign(NewDay); LabelDate.Caption := TheDay.GetText; NewDay.Free; end; Objects and MemoryMemory management in Delphi is subject to three rules, at least if you allow the system to work in harmony without Access Violations and without consuming unneeded memory:
Whether you must perform these operations in your code or can let Delphi handle memory management for you depends on the model you choose among the different approaches provided by Delphi. Delphi supports three types of memory management for dynamic elements:
Destroying Objects Only OnceIf you call the Free method (or call the Destroy destructor) of an object twice, you get an error. However, if you remember to set the object to nil, you can call Free twice with no problem. To sum things up, here are a couple of guidelines:
In general, you can also check whether an object is nil by using the Assigned function. The following two statements are equivalent in most cases: if Assigned (ADate) then ... if ADate <> nil then ... Notice that these statements test only whether the pointer is not nil; they do not check whether it is a valid pointer. If you write the following code, the test will be satisfied, and you'll get an error on the line with the call to the object method: ToDestroy.Free; if ToDestroy <> nil then ToDestroy.DoSomething; It is important to realize that calling Free doesn't set the object to nil. |
|
Copyright © 2004-2024 "Delphi Sources" by BrokenByte Software. Delphi Programming Guide |
|