Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/delphi/9.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Delphi:如何使多个虚拟方法链接到同一个函数?_Delphi_Iterator_Polymorphism - Fatal编程技术网

Delphi:如何使多个虚拟方法链接到同一个函数?

Delphi:如何使多个虚拟方法链接到同一个函数?,delphi,iterator,polymorphism,Delphi,Iterator,Polymorphism,我有一个抽象迭代器类: TImageIterator = class (TObject) protected fCurLine, fCurPos: Integer; public //also basic constructors here, etc. function ReadNextPixel: Cardinal; virtual; abstract; function ReadNextAsGrayscale: Cardinal; virtual; a

我有一个抽象迭代器类:

TImageIterator = class (TObject)
  protected
    fCurLine, fCurPos: Integer;
  public
    //also basic constructors here, etc.
    function ReadNextPixel: Cardinal; virtual; abstract;
    function ReadNextAsGrayscale: Cardinal; virtual; abstract;
    function ReadNextSubpixel: Cardinal; virtual; abstract;
end;
还有很多后代,比如,
t1bitmageiterator
t4bitmageiterator
t8bitmageiterator
T24BitRGBImageIterator
。关键是创建适合当前图像/像素格式的迭代器(在非矩形区域上),然后处理图像,而不管其类型如何

ReadNextPixel
返回“原始像素数据”:1位图像中的0或1,8位图像中的0..255或24位RGB中的某种颜色

readnextapgrayscale
返回0..255范围内像素的亮度,在8位图像的情况下,该亮度与
ReadNextPixel
相同

ReadNextPixel
在灰度/调色板图像中的工作原理与
ReadNextPixel
相同,但在24位RGB图像中返回R值,然后(在下一次调用中)返回G值,然后返回B值

现在我已经有了这样的实现:

function T8BitImageIterator.ReadNextPixel: Cardinal;
begin
  Result:=fByteLine[fCurPos];
  inch(fCurPos);
  if fCurPos=fRight then begin
    //going to next scanline, checking for end of iterated area etc
  end;
end;

//another 'unique' functions

//code we'd be happy to get rid of
function T8BitImageIterator.ReadNextAsGrayscale: Cardinal;
begin
  Result:=ReadNextPixel;
end;

function T1BitImageIterator.ReadNextSubpixel: Cardinal;
begin
  Result:=ReadNextPixel;
end;

function T4BitImageIterator.ReadNextSubpixel: Cardinal;
begin
  Result:=ReadNextPixel;
end;

function T8BitImageIterator.ReadNextSubpixel: Cardinal;
begin
  Result:=ReadNextPixel;
end;
  T8BitImageIterator = class (TImageIterator)
    public
      function ReadNextPixel: Cardinal; override;
      function ReadNextAsGrayscale=ReadNextPixel; override;
      ...
  end;
这些函数应该尽可能快,所以额外的函数调用看起来很难看,但是复制粘贴整个函数更糟糕

我想做的是这样的:

function T8BitImageIterator.ReadNextPixel: Cardinal;
begin
  Result:=fByteLine[fCurPos];
  inch(fCurPos);
  if fCurPos=fRight then begin
    //going to next scanline, checking for end of iterated area etc
  end;
end;

//another 'unique' functions

//code we'd be happy to get rid of
function T8BitImageIterator.ReadNextAsGrayscale: Cardinal;
begin
  Result:=ReadNextPixel;
end;

function T1BitImageIterator.ReadNextSubpixel: Cardinal;
begin
  Result:=ReadNextPixel;
end;

function T4BitImageIterator.ReadNextSubpixel: Cardinal;
begin
  Result:=ReadNextPixel;
end;

function T8BitImageIterator.ReadNextSubpixel: Cardinal;
begin
  Result:=ReadNextPixel;
end;
  T8BitImageIterator = class (TImageIterator)
    public
      function ReadNextPixel: Cardinal; override;
      function ReadNextAsGrayscale=ReadNextPixel; override;
      ...
  end;
但当然没有这样的语法

这可以通过接口来完成,但它们比虚拟函数调用慢得多。此外,还可以定义

TReadNextPixelProc = function: Cardinal of object;

TImageIterator = class(TObject)
  ...
  public
    ReadNextAsGrayscale: TReadNextPixelProc;
    ...
 end;
然后在构造函数中初始化这个变量,但这仍然会带来额外的成本,并使这些迭代器更难使用(我们必须记住什么是TReadNextPixelProc等)

我们还可以加速函数调用:

function T8BitImageIterator.ReadNextAsGrayscale: Cardinal;
asm
  jmp ReadNextPixel;
end;
因此,在执行
ReadNextPixel
之后,我们将不会返回到
ReadNextAsGrayscale
,而是返回调用
ReadNextPixel
的位置

但所有这些解决方案似乎都不正确:我看不出为什么两个VMT条目不能具有相同的函数指针,因此该函数只有一个副本可以“以任何其他名称”调用,而不需要额外的成本。有可能吗

这可以通过接口来完成,但它们比虚拟函数调用慢得多

不是真的。接口只不过是虚拟函数调用,因此调用函数的性能应该与显示的代码相同

也许你认为引用计数是瓶颈。您可以在接口实现类本身中或在传递接口指针的调用站点上禁用引用计数

接口是问题的解决方案。它们提供了一个显式语法,可以使用。例如:

type
  IImageIterator = interface
    function ReadNextPixel: Cardinal;
    function ReadNextAsGrayscale: Cardinal;
    function ReadNextSubpixel: Cardinal;
  end;

  TImageIteratorBase = class(TInterfacedObject, IImageIterator)
  protected
    fCurLine, fCurPos: Integer;
  public
    //basic constructors here, etc.
    function DoReadNext: Cardinal; virtual; abstract;
    function IImageIterator.ReadNextPixel = DoReadNext;
    function IImageIterator.ReadNextAsGrayscale = DoReadNext;
    function IImageIterator.ReadNextSubpixel = DoReadNext;
  end;

  T1BitImageIterator = class(TImageIteratorBase, IImageIterator)
  public
    function DoReadNext: Cardinal; override;
    // implement IImageIterator methods if needed...
  end;

  T4BitImageIterator = class(TImageIteratorBase, IImageIterator)
  public
    function DoReadNext: Cardinal; override;
    // implement IImageIterator methods if needed...
  end;

  T8BitImageIterator = class(TImageIteratorBase, IImageIterator)
  public
    function DoReadNext: Cardinal; override;
    // implement IImageIterator methods if needed...
  end;

  T24BitRGBImageIterator = class(TImageIteratorBase, IImageIterator)
  public
    function DoReadNext: Cardinal; override;
    // implement IImageIterator methods if needed...
  end;

...

function T1BitImageIterator.DoReadNext: Cardinal;
begin
  //...
end;

function T4BitImageIterator.DoReadNext: Cardinal;
begin
  //...
end;

function T8BitImageIterator.DoReadNext: Cardinal;
begin
  Result:=fByteLine[fCurPos];
  inch(fCurPos);
  if fCurPos=fRight then begin
    //going to next scanline, checking for end of iterated area etc
  end;
end;

function T24BitImageIterator.DoReadNext: Cardinal;
begin
  //...
end;
type
  ...
  T24BitRGBImageIterator = class(TImageIteratorBase, IImageIterator)
  public
    function DoReadNext: Cardinal; override;
    // implement IImageIterator methods if needed...
    function ReadNextAsGrayscale: Cardinal;
  end;

function T24BitRGBImageIterator.DoReadNext: Cardinal;
begin
  //...
end;

function T24BitRGBImageIterator.ReadNextAsGrayscale: Cardinal;
begin
  //...
end;
所有接口方法都通过一个
DoReadNext()
方法,子体可以根据需要覆盖该方法。如果派生类希望以不同的方式实现接口方法,它可以直接实现所需的方法,而忽略基类中的解析子句。例如:

type
  IImageIterator = interface
    function ReadNextPixel: Cardinal;
    function ReadNextAsGrayscale: Cardinal;
    function ReadNextSubpixel: Cardinal;
  end;

  TImageIteratorBase = class(TInterfacedObject, IImageIterator)
  protected
    fCurLine, fCurPos: Integer;
  public
    //basic constructors here, etc.
    function DoReadNext: Cardinal; virtual; abstract;
    function IImageIterator.ReadNextPixel = DoReadNext;
    function IImageIterator.ReadNextAsGrayscale = DoReadNext;
    function IImageIterator.ReadNextSubpixel = DoReadNext;
  end;

  T1BitImageIterator = class(TImageIteratorBase, IImageIterator)
  public
    function DoReadNext: Cardinal; override;
    // implement IImageIterator methods if needed...
  end;

  T4BitImageIterator = class(TImageIteratorBase, IImageIterator)
  public
    function DoReadNext: Cardinal; override;
    // implement IImageIterator methods if needed...
  end;

  T8BitImageIterator = class(TImageIteratorBase, IImageIterator)
  public
    function DoReadNext: Cardinal; override;
    // implement IImageIterator methods if needed...
  end;

  T24BitRGBImageIterator = class(TImageIteratorBase, IImageIterator)
  public
    function DoReadNext: Cardinal; override;
    // implement IImageIterator methods if needed...
  end;

...

function T1BitImageIterator.DoReadNext: Cardinal;
begin
  //...
end;

function T4BitImageIterator.DoReadNext: Cardinal;
begin
  //...
end;

function T8BitImageIterator.DoReadNext: Cardinal;
begin
  Result:=fByteLine[fCurPos];
  inch(fCurPos);
  if fCurPos=fRight then begin
    //going to next scanline, checking for end of iterated area etc
  end;
end;

function T24BitImageIterator.DoReadNext: Cardinal;
begin
  //...
end;
type
  ...
  T24BitRGBImageIterator = class(TImageIteratorBase, IImageIterator)
  public
    function DoReadNext: Cardinal; override;
    // implement IImageIterator methods if needed...
    function ReadNextAsGrayscale: Cardinal;
  end;

function T24BitRGBImageIterator.DoReadNext: Cardinal;
begin
  //...
end;

function T24BitRGBImageIterator.ReadNextAsGrayscale: Cardinal;
begin
  //...
end;

您不能单独对类执行同样的操作,因为没有语法。您必须在运行时直接攻击他们的VMT,例如在程序启动时。

声明一个执行此操作的私有内联方法,并从三个虚拟方法中的每一个调用它。内联引擎有时不能生成最有效的代码,因此请通过检查生成的代码来检查其执行情况


这种方法复制可执行文件中的代码,但避免在源代码中这样做。一个聪明的编译器/链接器将能够删除这种重复,但是Delphi工具从来没有这样的功能。这将是完全可能的黑客的VMT,但我个人不会走这条路线。我认为你不会有明显的收获

谢谢你的回答,我试着将我的迭代器实现为接口,一切都正常工作,非常好的代码,顺便说一下,不需要释放这些迭代器。只有一个问题:速度大约慢了两倍。旧的实现在200毫秒内处理映像,新的实现在450毫秒内处理映像。我在调试器中看过汇编代码,当从接口调用函数时,它不会直接调用,而是通过奇怪的代码,将接口指针转换为对象的“自”指针,然后通过“ret”命令以某种向后的方式调用我的函数!但这是在“good ol”delphi 7中,我现在来看看它在新版本中的表现,从那以后接口支持似乎有所改进。@Yuriy如果性能很关键,那么delphi可能是一个不好的选择,而在这样的核心级别(像素级别)的间接寻址将受到伤害。如果您可以在更高的级别上进行间接寻址,那么就不需要回答这个问题。也许在扫描线级别。@DavidHeffernan性能现在不是最重要的,它工作得相当快。易于开发和维护的代码更为重要。这些迭代器是最灵活的,允许处理非矩形区域,并在创建迭代器(许多基类的后代之一)时选择其形状,否则我将有太多的选择。当然,大多数简单的操作都是通过直接内存复制来实现的。你最后的评论让我觉得性能很重要。我喜欢你和雷米给出几乎相反的答案,两人都很棒!今天将尝试内联方法,似乎很有希望。我今天尝试过,效果非常好!我没有仔细观察生成的代码,但至少没有额外的函数调用,而且这个实现确实比从ReadNextAsGrayscale简单调用ReadNextPixel要快,正如前面提到的那样。非常感谢。