【问题标题】:Overloading the assignment operator for Object Pascal重载 Object Pascal 的赋值运算符
【发布时间】:2023-03-05 21:12:01
【问题描述】:

当赋值运算符:= 在 Object Pascal 中被重载时会发生什么?我主要是指首先评估什么,更重要的是如何(如果可能)更改此顺序。这是一个困扰我的例子:

我因此声明TMyClass

TMyClass = class
  private
    FSomeString: string;
    class var FInstanceList: TList;
  public
    function isValid: boolean;
    property SomeString: String write setSomeString;
  end;

isValid 函数检查 MyObject 是否有 nil 和悬空指针。

现在假设我想重载:= 运算符以将字符串分配给 TMyClass。我还想检查我分配这个字符串的对象是否是一个有效的对象,如果不是创建一个新的对象,那么:

operator :=(const anewString: string): TMyClass;
  begin
    if not(result.isValid) then
      result:= TMyObject.Create;
    result.SomeString:= aNewString;
  end;

简而言之,我希望结果会自动保存指向我要分配的对象的指针。但测试如下:

procedure TForm1.TestButtonClick(Sender: TObject);
  var
    TestObject: TMyObject;
  begin
    TestObject:= TMyObject.Create;
    TestObject:= 'SomeString';
    TestObject.Free;
  end;

让我相信,首先为result 分配一个中间值,而对TestObject 的实际分配发生在:= 中的代码执行之后。

我所知道的关于编码的一切都是自学的,但这个例子表明我显然在某个地方错过了一些基本概念。

我知道有比重载 := 运算符更简单的方法来做到这一点,但出于科学的好奇心,有什么方法可以让这段代码工作吗? (无论多么复杂。)

【问题讨论】:

  • 这在语法中清晰可见。结果不是参考,所以它是一个由你填写的临时值。不,我认为这不能通过运算符重载来完成。也许有一个默认属性。

标签: pascal lazarus freepascal


【解决方案1】:

使用运算符重载不可能做你想做的事。你必须使用一个方法。

问题在于:= 运算符无法让您访问左侧 (LHS) 参数(这里是Self,指向当前实例的指针)但是仅限于右手边参数。

目前在你的例子中if not(result.isValid) then 是危险的,因为函数开头的结果是未定义的(它可以有任何值,它可以是 nil 也可以不是 nil,当不是 nil 时,调用 isValid 将导致一些可能的违规访问。它根本不代表 LHS。

使用常规方法,您可以访问Self,并且可以调用isValid

【讨论】:

  • 谢谢。那正是我所想。但这是否意味着没有办法获得 LHS?使用inline 关键字怎么样?或者可能是一些低级汇编代码? (对我来说,汇编程序是纯粹的魔法,所以也许它可以在这里提供帮助。)你说得对,当然常规方法更有意义,但仍然......引用计数的作用与我想做的有点相似,我想?
  • 是的,就是这个意思。 Object Pascal 中的运算符重载不如其他语言。我特别想到另一个将重载直接编写为成员函数的方法,因此 self 始终可用。您也可以尝试在 FPC 邮件列表或 lazarus-ide.org 论坛上提问,也许那里会有更好的答案。
  • @N00BKING 我认为尝试以您想要的方式重载会混淆代码行为。在其他语言(例如 Ruby)中,您可以使用 x = x || y 或缩写 x ||= y 等操作完成您所要完成的工作,如果 xnil,则将 y 分配给 x。但是,正如 Nestedtype 指出的那样,Pascal 不允许您创建自己的运算符,这实际上是一种更好、更清晰的方式来做这个恕我直言。你被困在手头上,或者编写一个非常小的函数或方法来完成它,这也可以清楚地表达行为。
【解决方案2】:

我没有要检查的 Lazarus,但在 Delphi 中可以通过以下方式进行检查。我们通过TValue 间接访问该类的实例。

这是一个示例类:

  type
  TMyClass = class(TComponent)
  private
    FSomeString: string;
  published
    property SomeString: string read FSomeString write FSomeString;
  end;

并且我们在容器类中做了如下操作(例如TForm1)。

  TForm1 = class(TForm)
  private
    FMyClass: TMyClass;
    function GetMyTypeString: TValue;
    procedure SetMyTypeString(const Value: TValue);
  public   
    property MyClass: TValue read GetMyTypeString write SetMyTypeString;
  end;

...

function TForm1.GetMyTypeString: TValue;
begin
  Result := FMyClass;
end;

procedure TForm1.SetMyTypeString(const Value: TValue);
begin
  if Value.Kind in [TTypeKind.tkChar, TTypeKind.tkUString,
    TTypeKind.tkString, TTypeKind.tkWChar, TTypeKind.tkWString]
  then
  begin
  if not Assigned(FMyClass) then
    FMyClass := TMyClass.Create(self);
  FMyClass.SomeString := Value.AsString;
  end else
    if Value.Kind = TTypeKind.tkClass then
      FMyClass := Value.AsType<TMyClass>;
end;

在这种情况下,两个按钮单击都将正常工作。也就是说,它模拟:=重载:

procedure TForm1.Button1Click(Sender: TObject);
begin
  MyClass := 'asd';
end;

procedure TForm1.Button2Click(Sender: TObject);
begin
  MyClass := TMyClass.Create(self);
end;

以下是访问TMyClass 实例的方法:

procedure TForm1.Button3Click(Sender: TObject);
begin
  if Assigned(TMyClass(MyClass.AsObject)) then
    ShowMessage(TMyClass(MyClass.AsObject).SomeString)
  else
    ShowMessage('nil');
end;

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2013-03-30
    • 2013-02-14
    • 2016-08-30
    • 1970-01-01
    • 1970-01-01
    • 2013-12-08
    • 2018-05-13
    相关资源
    最近更新 更多