PDA

View Full Version : access violation, tobject



marmin
07-03-2008, 12:44 PM
Hi, I encountered asn access violation today, and it puzzles me. It may be simple but I can't figure it out.

I have declared a Master object




Tmasterobject = object

Sprite : array of TSprite;

End;

TIceblock = Object (Tmasterobject)

// lots of procs, etc. here.

End;

TEnemy = Object (Tmasterobject)

// lots of procs, etc. here.

End;



----------

Niow, when making a new Ticeblock by calling




New (Iceblock); // pointer to iceblock if of course TIceblock object


I get an access violation.

When making the array in TMasterobject fixed (f.e. array [0..4] of TSprite, the violation is not present.

How can it be?

marmin
07-03-2008, 03:45 PM
ah nevermind, I'll find another solution. can be locked.

arthurprs
07-03-2008, 05:04 PM
Try to specify the constructor (its the 2¬? parameter on new function)

arthurprs
07-03-2008, 05:05 PM
Try to specify the constructor (its the 2¬? parameter on new function)

User137
07-03-2008, 11:54 PM
What is this New() function you got? I made simple application that couldn't even compile with that objects:

procedure TForm1.FormCreate(Sender: TObject);
var sl: TStringList;
begin
new(sl);
//... add something here if even these worked
sl.Free;
end;

According to delphi help the New() is used to make pointer instance of a record. Furthermore, if you use dynamic arrays you must also use Setlength() to initialize it before use.

Normally it would go:
sl:=TStringList.Create;

marmin
08-03-2008, 08:19 AM
Exactly- that why it's faulty to use it. But, I don't want to use create/destroy (because Pascal won't let me create a totally new one 'on the fly' , but I do want to use the functionality of Objects. 'New' and Dispose, are old, but trusty, i like them,. And, I do not have to define them in advance.

AthenaOfDelphi
08-03-2008, 10:45 AM
Marmin,

I've been holding off replying because I'm trying to figure out what it is you want to achieve and I'm getting more confused with each post that appears.

If all you want to do is create an instance of the class TIceBlock, then this will work:-


var
newIceblock : TIceBlock
begin
newIceBlock:=TIceBlock.create;
...
...
newIceBlock.free;
end;


However, I'm guessing the problem is that you are getting AV's when you try and access the Sprite array. This will be because its not initialised.

So... if the array is the same size for all descendant classes, you can define it fully in the base class TMasterObject like this:-


type
TMasterObject = class(TObject)
public
Sprite : array of TSprite;
constructor create;
destructor destroy; override;
end;

constructor TMasterObject.create;
var
loop : integer;
begin
inherited;

setLength(Sprite,4);

for loop:=low(Sprite) to high(Sprite) do
begin
Sprite[loop]:=TSprite.create;
end;
end;

destructor TMasterObject.destroy;
var
loop : integer;
begin
for loop:=low(Sprite) to high(Sprite) do
begin
Sprite[loop].free;
end;

SetLength(Sprite,0);

inherited;
end;


Now, if you want to have different numbers of entries in the Sprites array for descendant classes, then this is one approach (put the common code in the base class and override specific parts in the descendants):-


type
TMasterObject = class(TObject)
protected
procedure initSprites(count:integer);
procedure deinitSprites;
public
destructor Destroy; override;

Sprite : array of TSprite;
end;

TIceBlock = class(TMasterObject)
public
constructor create;
end;

TEnemy = class(TMasterObject)
public
constructor create;
end;

procedure TMasterObject.destroy;
begin
try
deinitSprites;
except
end;

inherited;
end;

procedure TMasterObject.initSprites(count:integer);
var
loop : integer;
begin
setLength(Sprite,count);

for loop:=low(Sprite) to high(Sprite) do
begin
Sprite[loop]:=TSprite.create;
end;
end;

procedure TMasterObject.deinitSprites;
var
loop : integer;
begin
for loop:=low(Sprite) to high(Sprite) do
begin
Sprite[loop].free;
end;

SetLength(Sprite,0);
end;

constructor TIceBlock.create;
begin
inherited;

initSprites(4);
end;

constructor TEnemy.create;
begin
inherited;

initSprites(16);
end;



I have assumed TSprite is a class and I've initialised it. Now, you can create any number of instances of TIceBlock or TEnemy and they should have all their arrays configured and initialised with the objects.

You cannot use NEW and DISPOSE for creating objects. Whilst I guess in theory, the memory will be allocated, there is a whole bunch of stuff that goes on behind the scenes when an object is created.

I've just been pondering your statement 'And, I do not have to define them in advance' and I've just searched the forums and come across another post you made about this.

The reason why you have to declare instances was I think explained, but what wasn't provided were any solutions. So... here's one...


var
IceBlocks : TList;

...

IceBlocks:=TList.create;
IceBlocks.add(TIceBlock.create);

...

TIceBlock(IceBlocks[0]).free;
IceBlocks.delete(0);

...

IceBlocks.free;


Here, we define a TList. This will hold the references to our ice blocks. We create the list and add an ice block to it. At the end, we free up the ice block and remove it from the list and then finally free the list.

TList holds references to the generic TObject. You need to use typecasting the TIceBlock(...) part to tell the compiler that the object is actually a TIceBlock. If you split your objects into seperate list (as I've implied), then life is simple. But, you can put all your objects in one list and then check the type using the 'is' operator.


if ObjectList[loop] is TIceBlock then
begin
...
end
else
begin
if ObjectList[loop] is TEnemy then
begin
...
end;
end;


I hope this is helpful (if not to you, to others).

marmin
08-03-2008, 11:36 AM
Thanks for your post!
Yes, I see that I can use TList for creating instances of Iceblock. In fact, this would be the best approach. But.. I have a slight reluctance to use OOP too much.. I just want to control what is under the hood. So I have to make the decision to not using it all together or only use it, and not mixing it.
In fact, I do use New and Dispose an Object, and I get no errors when doing so. But i shouldn;t, obviously.

AthenaOfDelphi
08-03-2008, 04:06 PM
Hi marmin,

Please don't take offence, but I find this statement quite funny.



I have a slight reluctance to use OOP too much


I find it funny because you are using it (seemingly quite extensively from your sample code). You are just using OOP ala Turbo Pascal. Its how I first learned the concepts.

Is there a particular reason why you are reluctant to use classes (other than just wanting complete control)?

marmin
08-03-2008, 04:15 PM
Is there a particular reason why you are reluctant to use classes (other than just wanting complete control)?

No. The answer is very simple.
I want to understand and control the things I program. This is because I grew up using assembly language.
If I'd use oop 100 % I would not make these posts and have these silly errors. It is obvious it is not suited, entirely, for me. I still use oop, of course. But not for a 100 percent :D ;D

Thanks for reacting.

AthenaOfDelphi
08-03-2008, 04:40 PM
Thats pretty much what I figured :-)

When you say you grew up with assembly... are we talking 8 bit time, 16 bit time or some other time?

marmin
08-03-2008, 04:58 PM
MSX time .. :D
No oop present there.. :P We are spoiled today!

User137
08-03-2008, 05:20 PM
Exactly- that why it's faulty to use it. But, I don't want to use create/destroy (because Pascal won't let me create a totally new one 'on the fly' , but I do want to use the functionality of Objects. 'New' and Dispose, are old, but trusty, i like them,. And, I do not have to define them in advance.

It is faulty because:

myObject: TObject;

here myObject is just a "name" of some sort. Object's data in memory is not where this points to, data is elsewhere. Records work differently which is why you can use New() on them.
(You can see it yourself if you print out integer(@myObject) and integer(@myObject.[put some variable name here]) )

AND New() just couldn't know how much to allocate in memory. Sizeof() or any other method cannot tell how much memory an object uses.

And as mentioned before you don't have to put anything in constructor if you don't want to and still have full control.

marmin
10-03-2008, 11:33 AM
But I can use New () on Objects. I have checked for memory leaks, there are none.It seems, when there are no uncertain 'elements' , such as dynamic arrays, it is ok to use it on an object. In delphi, and also in freepascal, I get no errors. I think it only raises an exception when there is uncertain element in any object (such as dynamic array). Eventually, as I like to experiment much with programming, I may change all to records , and see how much memory usage is.

marmin
10-03-2008, 11:38 AM
I checked with delphi basics, this is what itm said:




The New procedure comes in two flavours.

The older version is an obsolete method of creating objects (you should now call the class constructor instead).

The first version allocates storage for a pointer type variable VariablePointer.

New is used when the storage is requirement is fixed in size. Use GetMem to dictate the exact storage size allocated.

Mirage
11-03-2008, 08:45 AM
You can use New() to create objects (not classes!).
But there is a nuance. AFAIR New() does not initialize memory as a class constructor does. It means that if you have a field which must be initialized before first use (strings, dynamic arrays, something more?) you should do it manually. It's enough to simply fill the memory allocated by New() with zeros.

User137
11-03-2008, 10:29 AM
Did a little pointer test that gave interesting results, some may find this useful:

type
TMyClass = class
var1: integer;
var2: byte;
end;

var
arr: array[0..3] of TMyClass;
test: TMyClass;

procedure TForm1.FormCreate(Sender: TObject);
var i: integer;
begin
for i:=0 to 3 do arr[i]:=TMyClass.Create;
test:=TMyClass.Create;
memo1.Lines.Add(
inttostr(integer(@arr))+#13+#10
+inttostr(integer(@arr[0]))+' '+inttostr(integer(@arr[0].var1))
+' '+inttostr(integer(@arr[0].var2))+#13+#10
+inttostr(integer(@arr[1]))+' '+inttostr(integer(@arr[1].var1))+#13+#10
+inttostr(integer(@arr[2]))+' '+inttostr(integer(@arr[2].var1))+#13+#10
+inttostr(integer(@arr[3]))+' '+inttostr(integer(@arr[3].var1))+#13+#10
+inttostr(integer(@test))+' '+inttostr(integer(@test.var1))
+' '+inttostr(integer(@test.var2))+#13+#10
+inttostr(sizeof(TMyClass))+#13+#10
+inttostr(sizeof(test))+#13+#10
+inttostr(test.InstanceSize)
);
test.Free;
for i:=0 to 3 do arr[i].Free;
end;


Prints out:

4533204
4533204 9517872 9517876
4533208 9517888
4533212 9517904
4533216 9517920
4533220 9517936 9517940
4
4
12

@arr
@arr[0] @arr[0].var1 @arr[0].var2
@arr[1] @arr[1].var1
@arr[2] @arr[2].var1
@arr[3] @arr[3].var1
@test @test.var1 @test.var2
sizeof(TMyClass)
sizeof(test)
test.InstanceSize

Ok the last bit actually does know size for class but couldn't find pointer to data area start (var1).

marmin
30-03-2008, 04:12 PM
Update.
I found it slightly puzzling why it would not work. It is not common practice, but it should work. As long as I use SetLength (), and I did, and stay within the array boundaries it should work.
So I tested the exact same code in Lazarus and FPC 2.2.1, and all is working fine.

And, I encountered another strange behaviour in Delphi 6 Personal (which I use) which costed me a day of frustration!. This was a fixed array! There should bo no problem with that.And still I got a strange , disgusting access violation error. I tested the code in FPC and it worked all fine.

Whast does this tell me. Is the Delphi 6 compiler outdated? Maybe the FPC compiler is smarter in allocating memory. Is it a good idea to update to turbo delphi or is the compiler the same?