programstinator
programstinator

Reputation: 1376

Is there a way to instantiate a desired number of objects in Delphi, without iterating?

I think that C++ supports something on the lines of :

Object objects[100];

This would instantiate a 100 objects, right? Is it possible to do this in Delphi (specifically 2007)? Something other than:

for i:=0 to 99 do
  currentObject = TObject.Create;

or using the Allocate function, with a passed size value a hundred times the TObject size, because that just allocates memory, it doesn't actually divide the memory and 'give' it to the objects. If my assumption that the c++ instantiation is instant rather than under-the-hood-iterative, I apologize.

Upvotes: 4

Views: 1190

Answers (5)

Remy Lebeau
Remy Lebeau

Reputation: 597285

It is somewhat possible in Delphi (but is not very practical in a Delphi environment, unless you are writing a custom memory manager). Creating an object instance is a two-step process - allocating memory for the object, and constructing the object's members inside of that memory. There is nothing requiring the memory of a given object to be allocated individually. You can allocate a larger block of memory and construct multiple objects inside of that block, taking advantage of a feature of Delphi that calls a constructor like a normal method if it is called from an instance variable instead of a class type, eg:

var
  objects: array of Byte;
  obj: TSomeClass;
begin
  SetLength(objects, 100 * TSomeClass.InstanceSize); 
  FillChar(objects[0], 0, Length(objects));
  for i := 0 to 99 do
  begin
    obj := TSomeClass.InitInstance(@objects[i * TSomeClass.InstanceSize]));
    obj.Create;
  end;
  ...
  for i := 0 to 99 do
  begin
    obj := TSomeClass(@objects[i * TSomeClass.InstanceSize]);
    obj.CleanupInstance;
  end;
  SetLength(objects, 0);
end;

This is not much different than what C++ does behind the scenes when declaring an array of object instances, only C++ supports declaring the array statically and it will call the constructors and destructors automatically, whereas Delphi does not support that.

There are a number of third-party implementations that allow you to allocate objects on the stack or in user-defined buffers, such as:

Objects on the Stack: A Delphi Relic

Allocate objects on stack, at specified memory address, or through any memory manager

Just to name a couple.

Upvotes: 1

Larry Lustig
Larry Lustig

Reputation: 51000

While you can't do what you want using objects, if your objects are relatively simple, you may be able to get what you want by using an array of records.

Records in Delphi can have properties (including setters and getters), and class and instance methods. They are created automatically when declared, so declaring an array of them will create them all without iterating.

For more info: http://docwiki.embarcadero.com/RADStudio/XE3/en/Structured_Types#Records_.28advanced.29.

(I'm not sure when the new functionality was added to Delphi, it may well be after the 2007 version).

Upvotes: 5

kludg
kludg

Reputation: 27493

What you are looking for is impossible because

  • Delphi does not support static (stack allocated) objects.
  • Delphi objects do not have default constructors that can be automatically invoked by compiler.

So that is not a lack of 'sugar syntax'.


For the sake of complete disclosure:

  • Delphi also supports legacy 'old object model' (Turbo Pascal object model) which allows statically allocated objects;
  • Dynamic object allocation itself does not prevent automatic object instantiation syntax, but makes such a syntax undesirable;
  • Automatic object instantiation syntax is impossible because Delphi does not have default constructors: Delphi compiler never instantiate objects implicitly because it does not know what constructor to call.

Upvotes: 5

GolezTrol
GolezTrol

Reputation: 116160

That declaration wouldn't create 100 objects, it would just give you an array of 100 object references that point to nothing useful.

Creating an object is a two step process. The first step is allocating memory (which your code also doesn't), the second step is calling the constructor (Create method) to initialize that memory, create additional objects, etc, etc.

The allocation part can be done without the loop, but the constructor needs to be called to intialize each instance.

Many VCL classes don't have an additional constructor. They just have the empty constructor that does nothing. In that case, there is no need to call it.

For instance, to fetch an array of stringlists, you can use the following code, adjusted from this example:

type
  TStringListArray = array of TStringList;v
var
  Instances: array of Byte;

function GetStringLists(Number: Integer): TStringListArray;
var
  i: Integer;
begin
  // Allocate instance memory for all of them
  SetLength(Instances, Number * TStringList.InstanceSize);
  // Zero the memory.
  FillChar(Instances[0], Length(Instances), 0);
  // Allocate array for object references.
  SetLength(Result, Number);

  for i := 0 to High(Result) do
  begin
    // Store object reference.
    Result[i] := @Instances[i * TStringList.InstanceSize];
    // Set the right class.
    PPointer(Result[i])^ := TStringList;
    // Call the constructor. 
    Result[i].Create;
  end;
end;

And to get an array of 100 stringlists:

var
  x: TStringListArray;
begin
  x := GetStringLists(100);

So while this procedure may save you a neglectable amount of time, and may theoretically be more memory-efficient (less fragmentation), you will still need the loop. No easy way out.

Upvotes: 1

Uli Gerhardt
Uli Gerhardt

Reputation: 14001

I don't know of any non-hacky way to do this besides iterating:

var
  MyObjects: array[0..99] of TMyObject;
  i: Integer;
begin
  for i := 0 to 99 do
    MyObjects[i] := TMyObject.Create;
end;

Upvotes: 2

Related Questions