我有抽象迭代器类:
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;
以及许多后代,例如,T1BitImageIterator
、T4BitImageIterator
、T8BitImageIterator
、T24BitRGBImageIterator
。重点是创建适合当前图像/像素格式的迭代器(在非矩形区域上),然后处理图像,无论其类型如何。
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/