2

I was wondering if it is possible to have an object instance without calling any constructor directly. Something like that:

var
  aObject : TMyObject;
begin
  aObject.MyMethod; //will cause an AV, but is it possible?
end;

Edit:

I know about static methods, but thats is not what i am looking for. I am looking for an way to get the constructor called without having to explicit call it.

4
  • 2
    What are you trying to achive? Commented Mar 23, 2011 at 13:26
  • 2
    It should only cause an AV if MyMethod refers to Self Commented Mar 23, 2011 at 13:30
  • 2
    It also AV's if MyMethod is virtual. Commented Mar 23, 2011 at 13:32
  • 2
    Can you explain why you want a constructor to be automatically called? Maybe we can provide a different solution. Do you need this for local (stack based) variables? If so, I'm 100% sure it can't be done. One might devise a way to automatically call constructors for class variables based on RTTI, but it would still require a call to a routine that initiates this process. Commented Mar 23, 2011 at 14:27

8 Answers 8

14

Delphi's objects are all heap-allocated: you can't simply declare an object and call methods on it, like you could do in C++ for example, you have to call an constructor to allocate and set up memory. But note even in C++ you are actually calling an constructor when you do that!

Maybe you can get away with a record and not an object? Example:

type
  TMyObject = record // name intentionally left as in OP's code
    procedure MyMethod;
  end;

procedure TMyObject.MyMethod;
begin
  // Do something
end;

// Use example:
procedure Test;
var MyObject: TMyObject; // TMyObject is a record so it is stack-allocated
begin
  MyObject.MyMethod; // Works.
end;
Sign up to request clarification or add additional context in comments.

1 Comment

+1 nice workaround, I just make a note, that it's possible since Delphi 2006
9

This is weird, as you know. But, so long as the method is static and doesn't refer to Self, then this will work.

Of course, such a thing is better known as a class method. Perhaps that's what you want?

type
  TMyObject = class
    class procedure MyClassMethod;
  end;
...
TMyObject.MyClassMethod;

EDIT

You edited your question. Apparently you know all about static methods. You don't want to call a method on an uninitialized object.

In fact, you now state that you want the act of calling a method on an un-initialized object instance to result in the instance being created and the method being called.

This is not possible. There's no magic anywhere that calls constructors for you.

5 Comments

Yeah, that is exactly what i was looking for. Thanks for you help, i will accept your solution since you said "This is not possible. There's no magic anywhere that calls constructors for you".
Actually, Barry Kelly (see my answer below) got RAII to work doing some clever interface tricks. It is pretty transparent.
@Jeroen That doesn't answer OP's Q. That code of Barry's calls a constructor explicitly.
Dang, I got that wrong from memory; I thought it was calling NewInstance and InitInstance in the background automagically. I do remember having read that, I can't just remember where :-(
I found it; actually you can, but you have to call NewInstance and InitInstance explicitly which you can wrap around in a factory method (like Barry did with the Create constructor). It is in a piece of code from a client, so I can't post the details though :-( In a way the RAII is a correct answer (like the accepted answer is), as you don't have to call the constructor explicitly at the call site. It is a matter how the OP defines explicitly :-)
3

You can "allocate" an object by using:

class function NewInstance: TObject; virtual;

This function allocates and initializes the object. After that, you can call the "create" constructor as a normal function on it (this is how constructor Create works when you dive into the assembly/CPU view)

2 Comments

That sound interesting, maybe you can explain it better to me or give a sample.
@Rafael Colucci, it's a way to get an object instance using two calls instead of one (you still need to call the constructor!). Might as well call the constructor alone.
2

There's a dirty trick:

THackClass = class
private
  function Sum(const A, B: Integer): Integer;
public
  function HackMethod: Integer;
end;

function THackClass.HackMethod: Integer;
begin
  Self := THackClass.Create;
  try
    Result := Sum(2, 3);
  finally
    Self.Free;
  end;
end;

function THackClass.Sum(const A, B: Integer): Integer;
begin
  Result := A+B;
end;

// and then

procedure TForm1.Button1Click(Sender: TObject);
var
  HackClass: THackClass;
begin
  ShowMessage(IntToStr(HackClass.HackMethod));
end;

Of course, it would be more natural to write it without the hack:

TNonHackClass = class
private
  function Sum(const A, B: Integer): Integer;
public
  class function NonHackMethod: Integer;
end;

class function TNonHackClass.HackMethod: Integer;
var
  instance: TNonHackClass;
begin
  instance := TNonHackClass.Create;
  try
    Result := instance.Sum(2, 3);
  finally
    instance.Free;
  end;
end;

function TNonHackClass.Sum(const A, B: Integer): Integer;
begin
  Result := A+B;
end;

// and then

procedure TForm1.Button1Click(Sender: TObject);
begin
  ShowMessage(IntToStr(TNonHackClass.NonHackMethod));
end;

11 Comments

But note that this trick does not work if HackMetod is a virtual method.
@Rafael: This is exactly what I told you yesterday! I stated: " so long as the method is static and doesn't refer to Self, then this will work" This answer uses Self in HackMethod but it would be better to do it with a local variable which would be equivalent.
The use of a local variable instead of Self is not equivalent. With non-initialized Self you can neither directly call any class methods (Sum in my example) nor access any fields.
@Roman a local variable of type THackClass could do all that. Using Self like that is utterly bizarre.
I don't think I should rollback your changes. I understand your doubts. Let the choice remain for Rafael Colucci.
|
2

Delphi - unlike for instance C++ - does not have a default way to automatically create constructors.

It has to do with how Delphi manages variables and scope.

In C++, when a variable comes into scope, it automatically calls the constructor.

What you are after is RAII (Resource Acquisition Is Initialisation).

Barry Kelly (one of the Delphi compiler engineers) wrote an interesting article about RAII in Delphi.

His solution is based on the automatic refcounting of interfaces in Delphi.

Explaining this would basically mean to copy/paste his article, so please follow the above link and read it.

--jeroen

Comments

1

No. There are actually several problems:

  1. you need to some how initialize the variable. Either to create the object, or to NIL it, so that the method can safely detect if the variable has been created or not
  2. If (1) doesn't create the object, the method invokation needs code to be generated to see if the object is created, and create it if necessary.
  3. Then there is the problem of destruction.

Interfaces fix 1 and 3, but since they are only NILed, (2) needs to happen, which can't be done afaik.

Such issues need to be implemented on compiler level. Without that, there are simply not events enough to implement it. But please do explain what exactly you want to achieve. Sometimes for specific cases, there are bearable workarounds, even if the general case is not fixable

Comments

0

As David said, you should look at Class methods, from the help:

Most methods are called instance methods, because they operate on an individual instance of an object. A class method is a method (other than a constructor) that operates on classes instead of objects. There are two types of class methods: ordinary class methods and class static methods.

Besides looking at class methods, you could use records, which now have the ability for methods.

unit test;

interface
uses
  SysUtils,
  Classes;

type
  TMyRecord = record
    FieldA:String;
    FieldB:String;
    procedure MyMethod(const Param1:Integer);
   end;

   procedure DoSomething(const Param:Integer);

implementation

procedure DoSomething(const Param:Integer);
var
  Rec:TMyRecord;
begin
  Rec.MyMethod(Param);  //no constructor needed
end;


procedure TMyRecord.MyMethod(const Param1:Integer);
begin
  //do something coolio here
end;

end.

4 Comments

I hope we will not get less qualified or even wrong answers in return of faster ones...
@Uwe - are you saying the answers are wrong? We were just joking that we were 30 seconds apart as it pops it up right when you are ready to hit send...what's your point?
@Darian: I didn't target the content of the answer or you and Cosmin directly, but I did find this issue at some other places. My comment was rather an overall observation and should express my concerns. Sometimes I get the impression that some people are just sitting there waiting for new questions to arrive. The link from The_Fox shows that I'm not alone.
0

You could also do something like this, though I fail to see why you would want to:

unit Unit2;

interface

type
  TMyClass = class
  public
    class function GetNew: TMyClass;
    function Hello: TMyClass;
  end;


implementation

{ TMyClass }

class function TMyClass.GetNew: TMyClass;
begin
  Result := TMyClass.Create();
end;

function TMyClass.Hello: TMyClass;
begin
  ShowMessage( 'hello' );
  Result := Self;
end;

procedure CallMyClass();
begin
  TMyClass.GetNew.Hello.Free;
  // or
  with TMyClass.GetNew do
  try
    Hello;
  finally
    Free;
  end;
end;

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.