2014-01-14 1 views
3

Пусть в Delphi у вас есть эти классы:Возможно ли создать «супернаследованный конструктор», вызывающий hiearchy не виртуальных конструкторов или методов?

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 объявить конструктор, который не является виртуальным.

Предположим, что я хочу вызвать конструктор-метод Create TClass1, изнутри TClass3.Create, но не вызывать конструктор-код в TClass2.Create? Возможно ли это на языке без обращения к RTTI?

Я не думаю, что есть такой синтаксис, но то, что я хочу это:

constructor TClass3.Create; 
begin 
    super inherited Create; // Invoke TClass1.Create 
end; 

Ближайший я могу получить это, который компилирует, но только просачивается объект, как это делает отдельный TClass1. Создайте конструкцию.

constructor TClass3.Create; 
begin 
    TClass1.Create; // returns new TClass1, discards and leaks it. 
    // other initialization here. 
end; 

Также мне кажется, что код TClass1.Create вызов в TClass3.Create компилирует, я не могу назвать это правильно, это неправильно, потому что утечка объект. Каков правильный правильный способ сделать это?

Обновление Обратите внимание, что ответ Дэвида работает для класса hiearchy без виртуальных конструкторов, только, как я изначально спросил. Его ответ не будет работать в вашем коде, если бы у вас были виртуальные конструкторы, а TClass2 и TClass3 переопределили их. Если бы я задал вышеуказанный вопрос с помощью виртуальных конструкторов (или виртуального метода, который не является конструктором), ответ будет «вы не можете сделать это вообще, за исключением действительно валовых виртуальных таблиц виртуальных методов». Также обратите внимание, что связанный «возможный дубликат» не является дубликатом, потому что ответ изменяется, когда вы добавляете/вычитаете виртуальные методы из ситуации.

+0

Я уверен, что вы можете обойти необходимость в такой конструкции, немного изменив иерархию классов, возможно, добавить общий базовый класс для 'TClass2' и' TClass3', полученный из 'TClass1' или что-то в этом роде. –

+1

Если бы это был весь мой код, я бы это сделал. Однако я стараюсь избегать переписывания сторонних компонентов. –

+1

Я думаю, что вызов типа 'TSomeClass.Create' выделяет память для экземпляра, а затем выполняет тело конструктора, тогда как вызовы типа« inherited Create »или' SomeInstance.Create' выполняют только тело на уже существующем экземпляре, поэтому 'TClass1.Create; 'не будет работать AFAICT. –

ответ

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. 

Выход

 
TClass1 
TClass3 
+0

Работает отлично здесь. Я использую ваши классы из вопроса, у которого нет виртуальных конструкторов. Конечно, реальный код, который я не вижу, использует виртуальные конструкторы, не так ли? ;-) –

+0

О, крысы. В моем реальном коде конструктор в TClass2 и TClass3 переопределен, а TClass1.Create является виртуальным. –

+0

Это не будет работать с виртуальными конструкторами - это приводит к переполнению стека. (Ой! Слишком медленно!) –

2

Пока вы не должны делать этот вы действительно можете добиться этого с помощью встроенного ассемблерного кода:

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

Но имейте в виду, что это фактически отличается от теоретического супернаследованного (который пропускает один уровень наследования), в то время как это просто вызывает указанный метод. Поэтому, если вы вводите еще один уровень TClass2b наследования между TClass2 и TClass3, он также пропустит это.

+0

Это действительно круто и страшно. Согласитесь, что вы не должны. Но приятно знать, как вы. Это имеет странное и интересное свойство обхода VMT/virtual-методов, если таковые имеются, скажем. –

+0

На самом деле это код, который производит компилятор для унаследованного в этом случае TClass2.Create, конечно :) –