Why is Self assignable in Delphi?

后端 未结 5 879
星月不相逢
星月不相逢 2021-01-04 02:17

This code in a GUI application compiles and runs:

procedure TForm1.Button1Click(Sender: TObject);
begin
  Self := TForm1.Create(Owner);
end;
<
相关标签:
5条回答
  • 2021-01-04 02:24

    That's not as bad as it could be. I just tested it in Delphi 2009, and it would seem that, while the Self parameter doesn't use const semantics, which you seem to be implying it should, it also doesn't use var semantics, so you can change it all you want within your method without actually losing the reference the caller holds to your object. That would be a very bad thing.

    As for the reason why, one of two answers. Either a simple oversight, or what Marco suggested: to allow you to pass Self to a var parameter.

    0 讨论(0)
  • 2021-01-04 02:24

    Assigning to Self is so illogical and useless that this 'feature' is probably an oversight. And as with assignable constants, it's not always easy to correct such problems.

    The simple advice here is: don't do it.

    0 讨论(0)
  • 2021-01-04 02:42

    In reality, "Self" is just a name reference to a place on the stack that store address pointing to object in the heap. Forcing read-only on this variable is possible, apparently the designer decided not to. I believe the decision is arbitrary.

    Can't see any case where this is useful, that'd merely change a value in stack. Also, changing this value can be dangerous as there is no guarantee that the behavior of the code that reference instance's member will be consistence across compiler versions.

    Updated: In response to PatrickvL comment

    The 'variable' "Self" is not on the stack (to my knowledge, it never is); Instead it's value is put in a register (EAX to be exact) just before a call to any object method is made. –

    Nope, Self has actual address on the memory. Try this code to see for yourself.

    procedure TForm1.Button1Click(Sender: TObject);
    begin
      ShowMessage(IntToStr(Integer(@Self)));
    end;
    
    procedure TForm1.Button2Click(Sender: TObject);
    var
      newform: TForm;
      p: ^Integer;
    begin
      Self.Caption := 'TheOriginal';
      newform := TForm.Create(nil);
      try
        newform.Caption := 'TheNewOne';
        // The following two lines is, technically, the same as
        //   Self := newform;
        p := Pointer(@Self);
        p^ := Integer(newform);
        ShowMessage(Self.Caption);  // This will show 'TheNewOne' instead of 'TheOriginal'
      finally
        Self.Free; // Relax, this will free TheNewOne rather than TheOriginal
      end;
    end;
    
    0 讨论(0)
  • 2021-01-04 02:46

    Sometimes, when you want to optimize a method for as far as you can take it (without resorting to assembly), 'Self' can be (ab)used as a 'free' variable - it could just mean the difference between using stack and using registers.

    Sure, the contents of the stack are most probably already present in the CPU cache, so it should be fast to access, but registers are even faster still.

    As a sidenote : I'm still missing the days when I was programming on the Amiga's Motorola 68000 and had the luxury of 16 data and 16 address registers.... I can't believe the world chose to go with the limited 4 registers of the 80x86 line of processors!

    And as a final note, I choose to use Self sometimes, as the Delphi's optimizer is, well, not optimizing that well, actually. (At least, it pales compared to what trickery one can find in the various LLVM optimizers for example.) IMHO, and YMMV of course.

    0 讨论(0)
  • 2021-01-04 02:51

    Maybe to allow passing to const or var parameters?

    It could be an artefact, since system doesn't have self anywhere on the left of := sign.

    0 讨论(0)
提交回复
热议问题