Recent

Author Topic: Object and reference to an object  (Read 12140 times)

luca

  • Jr. Member
  • **
  • Posts: 90
Object and reference to an object
« on: September 21, 2013, 02:58:39 pm »
I wrote following code to explain my doubt (TClass contains just a StringField):

Var a:TClass;
begin
  a:=TC.Create;
  a.StringField:='Luca';
  Caption:=a.StringField;
  a.free;
  a.StringField:='Test';
  Caption:=a.StringField;
end;

My question in Why after calling a.free I can continue to use the object?

Another example:

Var a,b:TClass;
begin
  a:=TC.Create;
  a.StringField:='Luca';
  Caption:=a.StringField;
  b:=a;
  freeAndNil(a); //destroy the reference too but
  b.StringField:='Test'; //I continue to reference the object by using b
  Caption:=b.StringField;
end;

In my mind free should destroy the object not the reference.

So why this code works?

Thanks

Martin_fr

  • Administrator
  • Hero Member
  • *
  • Posts: 12173
  • Debugger - SynEdit - and more
    • wiki
Re: Object and reference to an object
« Reply #1 on: September 21, 2013, 03:10:00 pm »
a.free

Does free the memory used by the object (It tells the memory manager, that it can re use that memory).

- It does not clear that memory
- it does not clear the local variable a
- if there is a destructor, it will call the destructor, but that does not change the 2 points above)

So if you access a, after a.free, then it may or may not work.
It will work, until the memory is actually re used, or otherwise changed. Once that happens you get errors,. All kind of random errors, from wrong results to sigsegv.


Use
FreeAndNil(a);

This will clear the local variable "a". But still not clear the memory of the object.

So
Code: [Select]
  a:=TC.Create;
  b := a;
  a.StringField:='Luca';
  freeandnil(a);
  b.StringField:='Test'; // still works, only a was cleared
  a.StringField:='Test'; // fails /crash

Yes, a, and b are actually references.

Also I advice, to use (during development / but not for releases)
-gh -gtt     for a better chance to get errors in a case like that.
-Criot

Add assertions to your code. (and use -Sa)


User137

  • Hero Member
  • *****
  • Posts: 1791
    • Nxpascal home
Re: Object and reference to an object
« Reply #2 on: September 21, 2013, 03:27:49 pm »
Code: [Select]
  a:=TC.Create;
  b := a;
  a.StringField:='Luca';
  freeandnil(a);
  b.StringField:='Test'; // (i am pretty sure this will crash too)
  a.StringField:='Test'; // fails /crash
If you free a data section, any pointer to it becomes invalid no? Well, if it would work it would be a memory leak and a critical error in the program.

hinst

  • Sr. Member
  • ****
  • Posts: 303
Re: Object and reference to an object
« Reply #3 on: September 21, 2013, 03:40:29 pm »
"works" with .Free for me, crashes with FreeAndNil()
just because something "works" does not mean one should do it. There are actually thousands of ways to insert a critical error in the program; it would be strange to expect the compiler to find all those errors while compiling application. THINK is what one should do while writing a program.
Too late to escape fate

Martin_fr

  • Administrator
  • Hero Member
  • *
  • Posts: 12173
  • Debugger - SynEdit - and more
    • wiki
Re: Object and reference to an object
« Reply #4 on: September 21, 2013, 04:24:45 pm »
If you free a data section, any pointer to it becomes invalid no?

"invalid" does not equal "protected from being used"

The pointer still exists., it still points to the same address in memory.
- This address of course by definition is no longer allowed to be accessed (it is not allocated)
- The content at that address is undefined/unknown. Unknown means, it can still make sense (e.g return some data, that does look like good data), or it can be unusable data.

So using an "invalid" pointer means unpredictable results. Unpredictable can be a crash, or can be something leading to any result, including a meaningful result.

hinst

  • Sr. Member
  • ****
  • Posts: 303
Re: Object and reference to an object
« Reply #5 on: September 21, 2013, 04:32:56 pm »
I am actually surprised not pleased that such questions appear. If one does not understand the basics of memory management, then one should consider reading some book like "programming for dummies" instead of making people on forum explain obvious things over and over again
Too late to escape fate

Martin_fr

  • Administrator
  • Hero Member
  • *
  • Posts: 12173
  • Debugger - SynEdit - and more
    • wiki
Re: Object and reference to an object
« Reply #6 on: September 21, 2013, 05:03:10 pm »
I am actually surprised not pleased that such questions appear. If one does not understand the basics of memory management, then one should consider reading some book like "programming for dummies" instead of making people on forum explain obvious things over and over again

Well I thing you owe the OT an apology. Besides being rude, you also (imho) misjudged complexity of the question.

The inner working of how objects are represented in memory are not "dummy" material. And the OT showed that he made an effort in understanding the issue as far as possible.

This is a very valid question.

hinst

  • Sr. Member
  • ****
  • Posts: 303
Re: Object and reference to an object
« Reply #7 on: September 21, 2013, 05:19:43 pm »
Using released pointer leads to undefined behavior... is that everyone should know before double clicking IDE icon on desktop
luca please do not be offended. And don't use destroyed|released objects|pointers. Just don't
Too late to escape fate

Martin_fr

  • Administrator
  • Hero Member
  • *
  • Posts: 12173
  • Debugger - SynEdit - and more
    • wiki
Re: Object and reference to an object
« Reply #8 on: September 21, 2013, 06:18:52 pm »
@hinst

Since we are talking about pointers (including those hidden by language constructs, such at TObject)...
And may I assume you have clicked the IDE Icon?

Any comments on the code below?

Code: [Select]
program Project1;
var txt: ansistring;

procedure Foo(const s: Ansistring);
begin
  txt := '';
  txt := s + '?';
end;

begin
  txt := 'Hello '+argv[1]+ ' How do you do';
  Foo(txt);
  writeln(txt);
end.

hinst

  • Sr. Member
  • ****
  • Posts: 303
Re: Object and reference to an object
« Reply #9 on: September 21, 2013, 07:40:20 pm »
@Martin_fr

Quote from: from FPC language reference
Remark: Note that specifying const is a contract between the programmer and the compiler. It is the programmer who tells the compiler that the contents of the const parameter will not be changed when the routine is executed, it is not the compiler who tells the programmer that the parameter will not be changed.

This is particularly important and visible when using refcounted types. For such types, the (invisible) incrementing and decrementing of any reference count is omitted when const is used. Doing so often allows the compiler to omit invisible try/finally frames for these routines.

As a side effect, the following code will produce not the expected output:
Var 
  S : String = ’Something’; 
 
Procedure DoIt(Const T : String); 
 
begin 
  S:=’Something else’; 
  Writeln(T); 
end; 
 
begin 
  DoIt(S); 
end.

Will write
Something else

This behaviour is by design.

I actually suspect that you took your "example" from the very same place too see if I read the FPC language reference or not. lol

I also did a bit of testing:

Code: [Select]
program Project1;

uses
  SysUtils;

function PointerToString(const p: Pointer): string;
begin
  result := IntToHex(PtrUInt(p), sizeof(p)*2);
end;

var
  txt: ansistring;

procedure Foo(const s: Ansistring);
begin
  WriteLN('Foo.1.s: ' + PointerToString(@s) + ' "' + s + '"');
  WriteLN('Foo.1.txt: ' + PointerToString(@txt) + ' "' + txt + '"');
  txt := '';
  WriteLN('Foo.2.s: ' + PointerToString(@s) + ' "' + s + '"');
  WriteLN('Foo.2.txt: ' + PointerToString(@txt) + ' "' + txt + '"');
  txt := s + '?';
  WriteLN('Foo.3.s: ' + PointerToString(@s) + ' "' + s + '"');
  WriteLN('Foo.3.txt: ' + PointerToString(@txt) + ' "' + txt + '"');
end;

begin
  txt := 'Hello '+argv[1]+ ' How do you do';
  WriteLN('main.1.txt: ' + PointerToString(@txt));
  Foo(txt);
  WriteLN('--------------------');
  writeln(txt);
end.


and the output is quite unexpected:
Code: [Select]
C:\Dev\hello-wut>project1.exe FFFUUU
main.1.txt: 00416000
Foo.1.s: 0141FF08 "Hello FFFUUU How do you do"
Foo.1.txt: 00416000 "Hello FFFUUU How do you do"
Foo.2.s: 0141FF08 "F
Foo.2.txt: 00416000 ""
Foo.3.s: 0141FF08 "F
Foo.3.txt: 00416000 "Foo.2.txt: 00416000 ""?"
--------------------
Foo.2.txt: 00416000 ""?

Just wanted to find out what is happening. [didn't understand a thing to be honest. Why is @s is different from @txt?] As you can see, the final output is Foo.2.txt: 00416000 ""? which looks pretty like undefined behavior.
So this code is bad. At first it may seem that it just virtually "replaces" const parameter contents with an empty string, but my example program shows that it most likely leads to completely undefined behavior. Most likely, because it drives FPC string manager mad. I don't know. To find out what exactly is happening one should take FPC string manager into account, and I don't know much about it, and, honestly, not in the mood for it right now

Well, making code which is hard to debug and understand is not that hard, but it is a bad idea; especially if other people are likely to have to deal with it later. The real challenge is to write clear code and documentation for it. lol
« Last Edit: September 21, 2013, 07:43:41 pm by hinst »
Too late to escape fate

User137

  • Hero Member
  • *****
  • Posts: 1791
    • Nxpascal home
Re: Object and reference to an object
« Reply #10 on: September 21, 2013, 10:00:14 pm »
You need to compare the data pointer
Code: [Select]
PointerToString(@s[1])@s is a pointer to pointer sort of, because s is dynamic array.

Martin_fr

  • Administrator
  • Hero Member
  • *
  • Posts: 12173
  • Debugger - SynEdit - and more
    • wiki
Re: Object and reference to an object
« Reply #11 on: September 21, 2013, 10:13:02 pm »
Actually I found that behaviour a few years back. I believe the clarifying sample in the doc was added after that. (Though it was correctly documented even without the sample)

It is not about this code is hard to read. (Or not meant to)

It is about knowing compiler implementation details. Something most people who click the IDE icon do not fully know.
And the point is that the internal representation of TObject (classes) as a pointer is not in the category of the very basics (though most people will find out sooner than later, and knowing it really helps.)
Also fyi there are objects that are not pointers
Code: [Select]
type
TFoo = object(TFooBase)
 //....
end;

But back to the code I posted (and sorry for hijacking the topic)

Yes
  txt := ''
does modify s, actually it modifies the string data to which s does point. So after that the pointer in s (ansistrings are pointers too) points to unallocated memory.


@s and @txt are different, those are the addresses of the 2 variables holding the pointer to the actual string.

Again, same as for objects @a and @b (for 2 objects after a := b) will be different, yet point to the same object data.

Since you pointed out so  knowingly that FreeAndNil(a) would not set b to nil, you should have seen that @txt would no be @s, or should you not?
Btw its rhetorical, lets just say this: I have seen people making mistakes with pointers, who are way more experienced than you and me together.
So blaming the OT for his question in my mind is still inappropriate.


The different between ansistrings and objects is, that with ansistrings you can not normally run into this trouble, because they are ref counted. classes are not refcounted (at least in fpc pascal / other languages other rules)

So my code employed a trick to bypass the ref counting.
I promised the compiler, I would not touch the value, and therefore the complier skipped ref counting. Then I broke my promise (I am deeply ashamed of that).
The result is exactly the object example, I am freeing the data of the string, clearing one of 2 pointers, leaving one dangling pointer.

luca

  • Jr. Member
  • **
  • Posts: 90
Re: Object and reference to an object
« Reply #12 on: September 21, 2013, 10:43:35 pm »
@Martin_fr
Thanks to clarify my doubt.

luca

  • Jr. Member
  • **
  • Posts: 90
Re: Object and reference to an object
« Reply #13 on: September 21, 2013, 10:56:07 pm »
@hinst
I'm not offended.
I  encountered this stuff right today and in my mind I supposed a different behaviour of the program. In any case I agree with you to don't use destroyed|released objects|pointers.

To everybody: Thanks for quick reply (GREAT community!)




taazz

  • Hero Member
  • *****
  • Posts: 5368
Re: Object and reference to an object
« Reply #14 on: September 21, 2013, 10:57:40 pm »
But back to the code I posted (and sorry for hijacking the topic)

Yes
  txt := ''
does modify s, actually it modifies the string data to which s does point. So after that the pointer in s (ansistrings are pointers too) points to unallocated memory.


and you do not consider that a compiler bug? As I see it this is unacceptable.

So my code employed a trick to bypass the ref counting.
I promised the compiler, I would not touch the value, and therefore the complier skipped ref counting. Then I broke my promise (I am deeply ashamed of that).
The result is exactly the object example, I am freeing the data of the string, clearing one of 2 pointers, leaving one dangling pointer.

This is BS, You do not promise anything to the compiler you instruct the compiler how to handle the data, when you declared something as a const you instruct the compiler to protect that data from changes even if a piece of code tries to change them the compiler must raise an error and stop. What you are saying sounds like premature optimization to me.

Take for example

Code: [Select]
// code assumes {$H+} or string = ansistring;
const
  MyText : String = 'Hello ';
var
  Txt : string;
begin
  Txt := MyText;
  Txt := Txt+'Wooooooooow';
end; 

would you expect the above code to change the data of MyText constant?
Good judgement is the result of experience … Experience is the result of bad judgement.

OS : Windows 7 64 bit
Laz: Lazarus 1.4.4 FPC 2.6.4 i386-win32-win32/win64

 

TinyPortal © 2005-2018