6
votes

Coming from a C++ background, I understand classes, pointers and memory addresses reasonably well. However, with Delphi, I am finding myself confused.

I understand that when you declare a variable of a particular type of class in the var section of a function/procedure, what you're really declaring is a POINTER to that class. E.g., the following Delphi and C++ are roughly equivalent, both allocating the amount of memory required for the MyObject class on the heap.

// Delphi
procedure Blah.Something();
var
  o: MyObject;
begin
  o := MyObject.Create;
  o.Free;
end;

// C++
void Blah::Something()
{
    MyObject *o = new MyObject();
    delete o;
}

In C++, using pointers (and references) allows the use of virtual methods for class hierarchies. However, if you don't have class hierarchies, you can declare a variable on the stack (which executes faster). If you need to pass the variable around as a pointer, you can simply get its address with the & operator.

// C++
void Blah::Something()
{
    // This is allocated on the stack.
    MyObject o;
    // This address of this stack-allocated object is being used.
    doSomethingWithAnOhPointer(&o);
}

At this stage, I have a few questions regarding Delphi's use of classes and pointers.

  1. If creating an object with o := MyObject.Create uses heap-allocation in Delphi, how do you allocate an object on the stack?
  2. If a variable of a specific type of class declared as o: MyObject is really a pointer, then why is the ^ pointer symbol never used. Is this a "convenience" idiom in Delphi?
  3. How can you get the address of the actual MyObject located on the heap? I have tried: the following.

    WriteLogLine('Address of object: ' + Format('%p', [@o])); // This prints the address of the 'o' pointer.
    WriteLogLine('Address of object: ' + Format('%p', [o])); // This causes the program to crash.
    

It's possible that I have misunderstood some Delphi fundamentals, but I have not found anyone (physically or on the Internet) who can explain the above to my satisfaction.

EDIT

Given that Delphi typically only allocates memory on the heap, then:

  1. Does assigning one object to another mean that they both point to the same address?
  2. Why does the assignment ("THIS ASSIGNMENT") not compile?

    procedure Blah.Something();
    var
      o1: MyObject;
      o2: MyObject;
      oP: ^MyObject;
    begin
      o1 := MyObject.Create;
      o2 := o1; // Both variables "o1" and "o2" point to the same object on the heap.
      WriteLogLine(Format('@o1 = %p, @o2 = %p', [@o1, %o2])); // This SHOULD produce two different address, as it's the address of the "implied pointer".
    
      oP := o1; // THIS ASSIGNMENT will NOT compile.
      WriteLogLine(Format('oP = %p', [oP]
    
      o1.Free;
      o1 := nil; // The single object has been deleted, but o1 = nil while o2 <> nil
    end;
    

(To give some context, there are multiple variables that should be pointing to the same object but may be pointing to different objects, so I want to compare their memory location to determine if this is the case.)

3
Minor nitpick re: the second C++ snippet: Due to the "most vexing parse", MyObject o(); declares a function, you certainly mean MyObject o;.user395760
@delnan: Minor nitpick, but that's not the most vexing parse (though it is a function declaration, like you say). The mvp looks something like this: type1 name(type2()); -- Where type1 is meant to be the type of a variable, but is actually the return type of a function, name is the name of the function, and type2() is meant to be a default constructed object, but is actually a parameter of a function type (which takes no arguments and returns a type2 object).Benjamin Lindley
Apologies re the ambiguity of the "most vexing parse". Fixed.magnus
Format('%p', [Pointer(o)])Sertac Akyuz
Sertac Akyuz, thank you. This answered question 3/5 perfectly.magnus

3 Answers

8
votes

If creating an object with o := MyObject.Create uses heap-allocation in Delphi, how do you allocate an object on the stack?

Delphi does not allow class instances to be allocated on the stack. They are always allocated on the heap.

If a variable of a specific type of class declared as o : MyObject is really a pointer, then why is the ^ pointer symbol never used. Is this a "convenience" idiom in Delphi?

Yes.

How can you get the address of the actual MyObject located on the heap?

Try this:

WriteLogLine('Address of object: ' + Format('%p', [Pointer(o)]));
3
votes

I am not great at delphi but here goes..

As opposed to Object Pascal, Delphi does not have stack allocation for objects.. Well not without using assembly that is.

You can indeed use the pointer syntax ^ but you'll have to use AllocMem or similar to allocate memory for the pointer:

Function AllocSomeClass: ^SomeClass;
begin
    Result := AllocMem(SizeOf(SomeClass); //FreeMem.
end;

Of course if the class has an init function or constructor or create function then simply doing SomeClass.Create is a better option.

As for using assembly, you can see these two:

http://www.kbasm.com/delphi-stack-local-object.html

and Is there a Delphi memory management function that allocates memory on the stack?

For printing addresses:

Format('%p %p %d', [@ptr, ptr, ptr^]);

Will display the address of ptr variable, the address stored in ptr and the value of ptr (data it points to) respectively.

Another option would be to use records instead of objects for stack allocation. Records are always on the stack unless specified otherwise. Objects require allocation or the calling of the create function/constructor.

Records cannot replace Objects but it's all I can think of at the moment.

1
votes

How can you allocate an object on the stack?

You cannot.

Why can ^ be omitted when accessing object members? Is it just a convenience.

Yes it is a convenience. In fact it can also be omitted when using a variable that is a pointer to a record.

How can I get the address of the object?

Use Format('%p', [Pointer(o)]).

Does assigning one object to another mean that they both have the same address?

Don't think of o1 := o2 as assigning one object to another. Instead think of this as assigning one reference to another, or assigning one pointer to another. Since an object variable is implemented as a pointer, this assignment merely assigns an address.

Why does oP := o1 not compile?

Because oP is of type ^MyObject which is double pointer. In C++ terms this code is akin to trying to assign an obj* to an obj**.