在非虚构造函数或方法的层次结构中,是否有可能进行“超级继承构造函数”调用?

3
假设在Delphi中你有以下这些类:
type
   TClass1 = class
     public
      constructor Create;
   end;

   TClass2 = class(TClass1)
     public
      constructor Create;
   end;

   TClass3 = class(TClass2)
     public
      constructor Create;
   end;

请注意,TClass1.Create不是虚拟的,并且TClass2和TClass3声明了一个非虚拟的构造函数。
假设我想从TClass3.Create中调用TClass1的Create构造方法,但不想调用TClass2.Create中的构造代码?在没有使用RTTI的情况下,这是否可能在语言内实现?
我认为没有这样的语法,但我想要的是:
constructor TClass3.Create;
begin
  super inherited Create; // Invoke TClass1.Create
end;

我能提供的最接近的答案是这个,尽管这段代码已经编译通过,但是它会泄露一个对象,因为它使用了一个单独的TClass1.Create构造函数。
constructor TClass3.Create;
begin
   TClass1.Create; // returns new TClass1, discards and leaks it.
   // other initialization here.
end;

我认为在TClass3.Create中调用TClass1.Create的代码TClass1.Create被编译通过,但这是错误的,因为它会泄漏一个对象。正确的方法是什么?更新请注意,David的答案只适用于没有虚构造函数的类层次结构,就像我最初提出的那样。如果您的代码具有虚构造函数并且TClass2和TClass3重写了它们,则他的答案将无法在您的代码中起作用。如果我问的问题涉及虚构造函数(或不是构造函数的虚方法),则答案是“除非使用非常丑陋的虚方法表技巧,否则根本无法实现”。还请注意,链接的“possible duplicate”不是重复的,因为当添加/删除虚方法时,答案会发生变化。

1
如果这是我的全部代码,我会这样做。不过,我尽量避免重写第三方组件库。 - Warren P
1
我认为像 TSomeClass.Create 这样的调用会为一个实例分配内存,然后执行构造函数体,而像 inherited CreateSomeInstance.Create 这样的调用只会在已经存在的实例上执行函数体,因此 TClass1.Create; 在我看来是行不通的。 - Uli Gerhardt
如果我理解正确,TClass1TClass2是第三方的,而您正在编写TClass3。对吗? - Uli Gerhardt
这很有帮助。我想我会坚持使用我的另一个丑陋的 hack(销毁并重新创建私有字段对象),而不是避免构造函数调用。 - Warren P
请注意虚拟和非虚拟之间的区别。 "duplicate" 是关于虚拟的。这个问题是关于非虚拟的。这种区别很重要,因为有一种方法可以通过使用类转换来调用父类的方法,但只有在它不是虚拟的时候才有效。因此,这提供了一个有趣的可搜索事实。请不要因未能理解这些细节而关闭此问题。 - Warren P
显示剩余3条评论
2个回答

2

在继承层次结构中没有语法支持跳过一层。你想要的唯一方法是这样:

TClass1(Self).Create;

一个完整的示例程序以进行演示:
type
  TClass1 = class
    constructor Create;
  end;

  TClass2 = class(TClass1)
    constructor Create;
  end;

  TClass3 = class(TClass2)
    constructor Create;
  end;

constructor TClass1.Create;
begin
  Writeln('TClass1');
end;

constructor TClass2.Create;
begin
  inherited;
  Writeln('TClass2');
end;

constructor TClass3.Create;
begin
  TClass1(Self).Create;
  Writeln('TClass3');
end;

begin
  TClass3.Create;
  Readln;
end.

Output

TClass1
TClass3

这里运行良好。我正在使用您在问题中提供的没有虚构造函数的类。当然,真正的代码,也就是我看不到的那部分,肯定使用了虚构造函数,对吧?;-) - David Heffernan
@GerryColl 确实。请注意问题中缺少虚拟构造函数。 - David Heffernan
1
不,只需提出一个新问题。不要混淆您提出的问题和您需要解决的问题。它们是不同的事情。 - David Heffernan
我认为你的回答和我的问题现在非常匹配。我接受了这个答案。 - Warren P
1
如果我将这个问题作为一个虚拟问题来问,那么这里链接的答案就是重复的……。https://dev59.com/Z2445IYBdhLWcg3w3N2z?lq=1 - Warren P
显示剩余6条评论

2

虽然你不应该这样做,但实际上你可以通过内联汇编代码实现:

constructor TClass3.Create;
begin
  asm
    mov eax, Self
    call TClass1.Create;
  end;
  Writeln('TClass3');
end;

但请记住,这与理论上的超级继承(跳过一个继承级别)实际上是不同的,因为这只是调用了该方法。因此,如果您在TClass2和TClass3之间引入另一个继承级别TClass2b,它也将被跳过。


这真的很酷,也有点吓人。同意你不应该这样做。但是了解一下你会怎么做还是挺好的。如果有的话,这个方法具有绕过VMT/虚拟方法的奇特而有趣的特性。 - Warren P
实际上,这就是编译器为继承中的TClass2.Create(在这种情况下)生成的代码 :) - Stefan Glienke

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接