delphi 操作符重载_Delphi XE2中的运算符重载示例

delphi 操作符重载

In my programming career I have only very rarely run into situations where operator overloading would be of any use in my work.  Normally those situations involved math with either overly large numbers (hundreds of thousands of digits or accuracy required) or matrix math.  Since I rarely have need for either, operator overloading has remained one of those areas that I thought would be an interesting area to try to get into.

在我的编程生涯中,我很少遇到操作员重载对我的工作有用的情况。 通常情况下,这些情况涉及的数学要么是数量过多(数十万个数字或所需的精度),要么是矩阵数学。 由于我几乎不需要二者之一,因此操作员重载一直是我认为将成为有趣的领域之一。

I recently had another coding requirement.  I mention it here because that need collided with the idea of operator overloading to make me consider operator overloading for an entirely new reason (to me).  I am writing a language parser.  This involves reading in characters one at a time and breaking them into tokens.  A big part of my coding along these lines, in the past, has been to make use of a Set Of Char.  This allows me to write very naturally flowing, easy to ready code such as:

我最近有另一个编码要求。 我在这里提到它是因为这种需要与运算符重载的思想相冲突,这使我出于全新的原因考虑运算符重载(对我而言)。 我在写语言解析器。 这涉及一次读取一个字符并将其分解为令牌。 过去,我沿这些方面进行编码的很大一部分是利用Set Of Char。 这使我可以编写非常自然的,易于编写的代码,例如:

function SkipWhiteSpace(var C : PChar) : boolean;
const
  WhiteSpace = [#9, #10, #13, ' '];

begin
  While (C^ <> #0) and (C^ in WhiteSpace) do
    inc(C);
  Result := C^ <> #0;  //fail if at the end
end;

This is simply an example of how things might have looked.  There are several things wrong with this code since Delphi 2009 introduced Unicode strings.

这只是情况看起来的一个例子。 自从Delphi 2009引入Unicode字符串以来,此代码存在一些问题。

If you attempt to use a set of character to do the same kind of test on characters in the newer versions of Delphi you get a warning:

如果尝试使用一组字符对较新版本的Delphi中的字符进行相同类型的测试,则会收到警告:

W1050 WideChar reduced to byte char in set expressions.  Consider using 'CharInSet' function in 'SysUtils' unit.

W1050 WideChar在集合表达式中减少为字节char。 考虑在“ SysUtils”单元中使用“ CharInSet”功能。

To avoid this, you could change:

为避免这种情况,您可以更改:

(C^ in WhiteSpace)
(CharInSet(C, WhiteSpace))

Different varieties of Unicode strings may contain characters that are 1-4 bytes wide.  For this reason, instead of

不同种类的Unicode字符串可能包含1-4字节宽的字符。 因此,代替

Inc(C);
C := StrNextChar(C);

The code could also have been written to make use of streams, keep in mind this is written to focus on the issue at hand.  While there are valid concerns about the code, try to keep focused on where I am heading and leave the analysis for later.

也可以编写代码来利用流,请记住,编写此代码的重点是当前的问题。 尽管对代码存在真正的担忧,但请尝试专注于我要去的地方并将分析留给以后使用。

A Delphi set type is limited to 256 elements due to the fact that each element is represented by one bit (resulting in a 32 byte type).  With 65,536 possibilities in a 2 byte character this would result in an 8k set variable since a set requires all of the space, even when empty.

由于每个元素都由一位表示(导致32字节类型),因此Delphi集类型限制为256个元素。 在2字节字符中有65,536种可能性,这将导致8k的set变量,因为set需要所有空间,即使是空的也是如此。

While the new notation works, I find it makes the code less natural to read.  Doing things the old way will generate warnings, you could ignore those, it also limits the sets to using Ansi (8 byte) characters.  I suppose I am like most people in that I wanted to have my cake and eat it too.

尽管新的符号有效,但我发现它使代码的可读性降低。 用旧的方式处理会生成警告,您可以忽略这些警告,这也将设置限制为使用Ansi(8字节)字符。 我想和大多数人一样,我也想吃蛋糕。

To that end I decided to use operator overloading to create a new kind of set.  The idea was to use a record that stores a string containing the characters that are in the set while allowing it to use the normal set operations.  To start with you declare a normal record type, in this case I chose not to use the usual capital T to start the name since this is intended to be a base type and is not a class.

为此,我决定使用运算符重载来创建一种新的集合。 想法是使用一条记录,该记录存储一个字符串,该字符串包含集合中的字符,同时允许它使用常规的集合操作。 首先,您声明一个正常的记录类型,在这种情况下,我选择不使用通常的大写字母T来开头名称,因为它旨在作为基本类型而不是类。

type
  WideCharSet = record
    Chars : string;
    class operator Add(a, b: WideCharSet): WideCharSet;
    class operator Implicit(a : WideCharSet) : string;
    class operator Implicit(a : string) : WideCharSet;
  end;

I started by using a member variable called Chars as a string to hold my list of characters in my set.  The implicit class operators allow me to assign back and forth between a string and a WideCharSet.  I decided to start with the Add class operator as my first test.  When assigning from a string I did not want to just set a string of characters into the Chars member variable.  I wanted to assume I was assigning a string containing something like a normal set declaration.  In this case, to make it easier to work with, I allowed the user to denote characters with single or double quotes.  Here is the code for the Implicit conversion from string to WideCharSet the full source code will be at the end of the article so, for now, don't worry about any missing code.

我首先使用一个称为Chars的成员变量作为字符串来保存我的字符集。 隐式类运算符允许我在字符串和WideCharSet之间来回分配。 我决定从Add类运算符开始,这是我的第一个测试。 从字符串分配时,我不想仅将字符字符串设置为Chars成员变量。 我想假设我正在分配一个字符串,其中包含类似正常set声明的内容。 在这种情况下,为了简化操作,我允许用户用单引号或双引号表示字符。 这是从字符串到WideCharSet的隐式转换的代码,完整的源代码将在本文的结尾,因此,现在,不必担心缺少任何代码。

class operator WideCharSet.Implicit(a : string) : WideCharSet;
var
  I : integer;
  C, D, E : Char;

begin
  Result.Chars := '';
  //have to have at least []
  assert(Length(a) >= 2, 'Invalid string used in convert to SetOfWideChar');

  I := 1;
  SkipWhiteSpace(a, I);
  assert(a[I] = '[', 'Invalid string used in convert to SetOfWideChar, ' +
    'missing opening square bracket');

  inc(I);

  while (I < Length(a)) and (a[I] <> ']') do
    begin
      C := ReadChar(a, I);
      SkipWhiteSpace(a, I);
      if a[I] = '.' then
        begin
          inc(I);
          if I > Length(a) then
            raise Exception.Create(InvalidSetOfWideCharString);
          if a[I] <> '.' then
            raise Exception.Create(InvalidSetOfWideCharString);
          inc(I);
          D := ReadChar(a, I);

          for E := C to D do
            InsertSorted(E, Result.Chars);
        end
      else
        InsertSorted(C,Result.Chars);

      if a[I] = ',' then
        begin
          inc(I);
          if I > Length(a) then
            raise Exception.Create(InvalidSetOfWideCharString);
        end;
    end;

  Assert(I <= Length(A), InvalidSetOfWideCharString);
  Assert(a[I] = ']', InvalidSetOfWideCharString);
end;

There is quite a bit to talk about in this method.  I used assertions to enforce the style of the string passed into the conversion routine, that way they could be compiled out easily.  I included the skipping of white space to make it more friendly for developers to use.  Keep in mind that this is a first attempt.  I may change the assertions to exceptions if I find that to be more useful in some situations.

这种方法有很多要讨论的地方。 我使用断言来强制传递到转换例程中的字符串的样式,以便可以轻松地将它们编译出来。 我包括跳过空格,以使其对开发人员更加友好。 请记住,这是第一次尝试。 如果我发现断言在某些情况下更有用,则可以将其更改为异常。

I am doing some parsing in this method.  This allows me to be flexible regarding the precise format of the string being passed in.  To that end the class includes methods such as SkipWhiteSpace and ReadChar.  SkipWhiteSpace just moves past any spaces, tabs, carriage returns, and line feeds.  ReadChar does a bit more than it might seem.  It reads in the definition of a character as defined in a string.  This means either a single character in quotes, a # followed by a decimal number, or #$ followed by a hex definition.  I later realized that just adding the characters to the end of the Chars member variable would not allow me to compare sets.  Realizing this, I added the method InsertSorted.  If all of the characters are added in a sorted manner then, no matter how the set was built, comparisons should work accurately.

我正在这种方法中进行一些解析。 这使我可以灵活地处理传入的字符串的确切格式。为此,该类包括诸如SkipWhiteSpace和ReadChar之类的方法。 SkipWhiteSpace只是移过任何空格,制表符,回车符和换行符。 ReadChar的作用超出了看起来。 它读取字符串中定义的字符的定义。 这意味着要么用引号引起来,要么在#号后面加上一个十进制数字,要么在#$后面加上一个十六进制定义。 后来我意识到,仅将字符添加到Chars成员变量的末尾将不允许我比较集合。 意识到这一点,我添加了方法InsertSorted。 如果所有字符都按排序方式添加,则无论集合如何构建,比较都应正确进行。

Of course I could not define the class to accept strings in and not allow the set to be assigned to a string.  This assignment would require the parsing of the current set of characters to create a minimal definition for the set.  Here is the code for the implicit assignment of a WideCharSet to a string.

当然,我无法定义该类以接受字符串,也不允许将集合分配给字符串。 此分配需要解析当前字符集,以为该字符集创建最小定义。 这是将WideCharSet隐式分配给字符串的代码。

class operator WideCharSet.Implicit(a : WideCharSet) : string;
var
  Line : string;
  I : integer;
  FirstChar, LastChar : Char;
  Start : integer;
  R1, R2 : string; //possible range representations for characters

begin
  Line := '';

  {Start at the first character and see how far we can go until we run into a
   non sequential character}
  I := 1;
  While I < Length(a.Chars) do
    begin
      Start := I;
      FirstChar := a.Chars[I];
      repeat
        inc(I);
      until (I > Length(a.Chars)) or
        (ord(a.Chars[I]) <> Ord(FirstChar) + I - Start);

      LastChar := a.Chars[Pred(I)];

      if Line <> '' then
        Line := Line + ', ';

      R1 := CharToStringRep(FirstChar);
      Line := Line + R1;

      if I > Start + 1 then
        begin
          R2 := CharToStringRep(LastChar);
          Line := Line + '..' + R2;
        end;
    end;

  Result := '[' + Line + ']';
end;

The only piece of this code I would think might need some extra explanation is CharToStringRep.  Not all characters are legible or identifiable as they are.  Some work much better as a literal numeric definition.  This function returns the character as it should be represented.  The first part inside the loop is used to find the end of a run of characters.  If no run is found then the character is added to the line.  If a run was found then it adds the first character, '..' and the last character to the line.  The process is repeated until all characters have been accounted for.

我认为这段代码中唯一需要做一些额外的解释的就是CharToStringRep。 并非所有字符都一样清晰可辨。 有些可以作为文字数字定义更好地工作。 此函数返回应表示的字符。 循环内部的第一部分用于查找字符序列的结尾。 如果找不到运行,则将字符添加到行中。 如果找到运行,则将第一个字符“ ..”和最后一个字符添加到该行。 重复该过程,直到已考虑所有字符。

Next I wanted to be able to test for membership with the IN operator.  

接下来,我希望能够使用IN运算符测试成员资格。

class operator WideCharSet.In(a : Char; b : WideCharSet) : Boolean;
begin
  Result := SortedPos(a, b.Chars) <> 0;
end;

While I could have used the Pos function I thought it made more sense to use the same CharSearch function I use when adding to a set.  To that end I added the SortedPos function which acts just like the Pos function except it works on sorted strings of characters, using a binary search.  On large sets it should be a lot faster.  On smaller sets it should be similar speed.  If you know that your code will only be using extremely small sets you might want to switch it to use the Pos function.  Since the CharSearch function is intended to return either the index of the character, or the index of where it should be inserted, the function needed to take a couple of extra precautions (checking the the index was not past the end of the string, and that it matched).

虽然本可以使用Pos函数,但我认为使用与添加到集合中时相同的CharSearch函数更有意义。 为此,我添加了SortedPos函数,该函数的功能类似于Pos函数,只是它使用二进制搜索在排序的字符串上起作用。 在大型机器上,它应该快得多。 在较小的机器上,其速度应该相似。 如果您知道代码将仅使用极小的集合,则可能需要将其切换为使用Pos函数。 由于CharSearch函数旨在返回字符的索引或应插入字符的索引,因此该函数需要采取一些额外的预防措施(检查索引是否未超过字符串的末尾,以及匹配)。

At this point I can already do some basic code using my new type.

至此,我已经可以使用新类型执行一些基本代码了。

Procedure MyTest;
var
  Numeric : WideCharSet;
  WhiteSpace : WideCharSet;
  C : Char;

begin
  WhiteSpace := '[#9..#10, #13, " "]';
  Numeric := '["0".."9"]';
  C := '6';
  if C in WhiteSpace then
    begin
      //obviously not
    end;

  if C in Numeric then
    begin
      //Bingo
    end;
end;

Here you can see assignment from strings as well as tests for membership.

在这里,您可以查看字符串分配以及成员资格测试。

From this point, the remainder of the work is in assigning more of the operator overloading class methods.  The ones we still need in order to do most of the same work as a Delphi set of char would be:

从这一点出发,剩下的工作是分配更多的运算符重载类方法。 为了完成与Delphi字符集相同的大部分工作,我们仍然需要以下这些:

    class operator Add(a, b: WideCharSet): WideCharSet;
    class operator Subtract(a, b: WideCharSet): WideCharSet;
    class operator Multiply(a, b : WideCharSet) : WideCharSet;
    class operator Equal(a, b: WideCharSet) : Boolean;
    class operator LessThanOrEqual(a, b : WideCharSet) : Boolean;
    class operator GreaterThanOrEqual(a, b : WideCharSet) : Boolean;
    class operator NotEqual(a, b : WideCharSet) : Boolean;

  Here is a more complete test of the functionality.

这是对该功能的更完整测试。

Procedure MyTest;
var
  Numeric : WideCharSet;
  WhiteSpace : WideCharSet;
  LowerCaseAlpha : WideCharSet;
  UpperCaseAlpha : WideCharSet;
  Alpha : WideCharSet;
  AlphaNumeric : WideCharSet;
  CommonNumericAlphaNumeric : WideCharSet;
  CommonUpperAndAlphaNumeric : WideCharSet;
  AlphaNumericMinusNumeric : WideCharSet;
  AlphaNumericDefinition : string;

begin
  WhiteSpace := '[#9..#10, #13, " "]';
  Numeric := '["0".."9"]';
  LowerCaseAlpha := '["a".."z"]';
  UpperCaseAlpha := '["A".."Z"]';
  Alpha := LowerCaseAlpha + UpperCaseAlpha;
  AlphaNumeric := Alpha + Numeric;
  CommonNumericAlphaNumeric := Numeric * AlphaNumeric;
  CommonUpperAndAlphaNumeric := UpperCaseAlpha * AlphaNumeric;
  AlphaNumericMinusNumeric := AlphaNumeric - Numeric;

  AlphaNumericDefinition := AlphaNumeric + '["&"]';
end;

I will leave it to the reader to go over the individual methods as they are relatively common programming.

我将它留给读者阅读,因为它们是相对通用的编程方法。

One type of operation that this type of set would not be able to do (when compared to a Delphi set) is to use the functions Include and Exclude.  Those could be added as methods to the class if the functionality is desired.  This would not produce equivalent code to the set of char, but it should be familiar enough to provide the functionality well.  I will leave that as an exercise for the reader.

这种类型的集合无法进行的一种操作类型(与Delphi集合相比)是使用包含和排除功能。 如果需要功能,可以将它们作为方法添加到类中。 这不会产生与char集合等效的代码,但是应该足够熟悉才能很好地提供功能。 我将其留给读者练习。

This article should have been able to show you that using operator overloading in Delphi is not difficult, but it does have some limitations.  One is not being able to dynamically create space for member variables.  It is also limited to being used with records (one of the reasons that it cannot dynamically create space for member variables).  The help on operator overloading in Delphi mentions both that it only works with records, and that it can be used with records and classes.  I can tell you that I tried adding an operator overloading method to a class and got a compiler error.

本文应该能够向您展示,在Delphi中使用运算符重载并不困难,但确实有一些限制。 一种是不能动态地为成员变量创建空间。 它也仅限于与记录一起使用(它不能为成员变量动态创建空间的原因之一)。 Delphi中有关运算符重载的帮助提到它仅适用于记录,并且可以与记录和类一起使用。 我可以告诉你,我尝试将运算符重载方法添加到类中并遇到编译器错误。

The help on the topic in Delphi XE2 can be found using the help address:

可以使用帮助地址找到有关Delphi XE2中该主题的帮助:

ms-help://embarcadero.rs_xe2/rad/Operator_Overloading_(Delphi).html

ms-help://embarcadero.rs_x e2 / rad / Ope rator_Over loading_(D elphi.htm 升

The help states that you should not provide implicit conversions both from type A to type B and from type B to type A.  In my own code it did not present a problem, but that may be due to the fact that on of the types was a Delphi base type.

该帮助指出,您不应该同时提供从类型A到类型B以及从类型B到类型A的隐式转换。在我自己的代码中,它没有出现问题,但这可能是由于以下事实导致的: Delphi基本类型。

uWideCharSet.pas uWideCharSet.pas

翻译自: https://www.experts-exchange.com/articles/10088/An-example-of-Operator-Overloading-in-Delphi-XE2.html

delphi 操作符重载

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值