gpt4 book ai didi

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

转载 作者:行者123 更新时间:2023-12-03 15:23:19 25 4
gpt4 key购买 nike

我有抽象迭代器类:

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;

以及许多后代,例如,T1BitImageIteratorT4BitImageIteratorT8BitImageIteratorT24BitRGBImageIterator。重点是创建适合当前图像/像素格式的迭代器(在非矩形区域上),然后处理图像,无论其类型如何。

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

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

ReadNextSubpixel 与灰度/调色板图像中的 ReadNextPixel 工作方式相同,但返回 R 值,然后(在下一次调用中)G 值,然后返回 24 位 RGB 中的 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;

但是当然没有这样的语法。

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

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 条目不能具有相同的函数指针,因此只有一个该函数的副本,该副本被“以任何其他名称”调用,而无需额外开销。可以做到吗?

最佳答案

It can be done with interfaces, but they are much slower then virtual functions calls

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

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

接口(interface)是您问题的解决方案。它们提供了明确的语法来准确地执行您所要求的操作,使用 Method Resolution Clause 。例如:

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;

所有接口(interface)方法都通过单个 DoReadNext() 方法,后代可以根据需要覆盖。如果派生类想要以不同的方式实现接口(interface)方法,它可以直接实现所需的方法,忽略基类中的解析子句。例如:

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:如何使多个虚拟方法链接到同一个函数?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/34446169/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com