2016-03-21 15 views
6

Kompilując następujący kod pojawia się błąd:Czy metody interfejsu są zawsze wirtualne?

TOmniParallelSimplePooledLoop = class(TOmniParallelSimpleLoop) 
    procedure Execute(loopBody: TOmniIteratorSimpleSimpleDelegate); overload; override; 

[dcc64 Error] OtlParallel.pas(846): E2170 Cannot override a non-virtual method

Jeśli zrobię metoda przodek wirtualny to błąd zniknie.

Jednak metoda przodek jest zadeklarowana w:

IOmniParallelSimpleLoop 
    ... 
    procedure Execute(loopBody: TOmniIteratorSimpleSimpleDelegate); overload; 

będzie ponowna deklaracja metody bazowej w TOmniParallelSimpleLoop od nie-wirtualnych do wirtualnej zmiany typu bazowego, lub była metoda już wirtualny zacząć (z powodu czy jest to implementacja metody interfejsu)?

Innymi słowy: czy kompilator wypisze inny kod, gdy zmieniona metoda zostanie zmieniona z innej niż wirtualna na wirtualną?

Podstawowe MSVC odtworzyć błędu

program Project70; 
{$APPTYPE CONSOLE} 
uses 
    System.SysUtils; 

type 
    I1 = interface 
    procedure DoSomething; 
    end; 

    T1 = class(TInterfacedObject, I1) 
    procedure DoSomething; 
    end; 

    T2 = class(T1) 
    procedure DoSomething; override; 
    end; 

procedure T1.DoSomething; 
begin 
    WriteLn('parent'); 
end; 

procedure T2.DoSomething; 
begin 
    Writeln('Child'); 
end; 

begin 
end. 
+1

Prawdziwym pytaniem, które powinieneś zadać, jest to, jak Delphi kompiluje T1. Jeśli T1.DoSomething jest statyczny. Wszystkie książki COM stwierdzają, że COM = VMT. W jaki sposób T1.DoSomething dostaje się do VMT? Myślę, że to jest miejsce, w którym dzieje się twoje zamieszanie. Ponieważ nie jest to klasa VMT dla wirtualnych wywołań metod, ale VMT, która mapuje metody, czy ich statyczny, wirtualny lub dynamiczny, więc interfejs wie, do jakiej metody wywołać ... Oto świetna artykulacja, która pokazuje, jak zaimplementować interfejs bez obiektu . https://sergworks.wordpress.com/2012/04/01/interfaces-without-objects/ –

Odpowiedz

8

Are interface methods always virtual?

Metody wirtualne interfejsy są ani ani nie wirtualnym. Ta koncepcja nie dotyczy metod interfejsów.

Z drugiej strony metody klas mogą być wirtualne lub nie-wirtualne. Rozróżnienie określa sposób wiązania wywołań metod. Metody wirtualne są wiązane w czasie wykonywania, biorąc pod uwagę typ środowiska wykonawczego obiektu. Metody nie-wirtualne są powiązane podczas kompilacji, używając typu czasu kompilacji odwołania do obiektu.

Błąd kompilatora mówi po prostu, że override ma znaczenie tylko dla metod wirtualnych. Twój kod próbuje użyć override dla metody, która nie jest wirtualna. Rozważmy ten program, który w ogóle nie zawiera żadnych interfejsów:

type 
    T1 = class 
    procedure DoSomething; 
    end; 

    T2 = class(T1) 
    procedure DoSomething; override; 
    end; 

procedure T1.DoSomething; 
begin 
end; 

procedure T2.DoSomething; 
begin 
end; 

begin 
end. 

Ten program nie może skompilować się z dokładnie tym samym błędem, co program. Błąd nie ma związku z interfejsami. Deklaracja, że ​​DoSomething będzie wirtualna, gdy zostanie wprowadzona w T1, rozwiąże problem.

Will the redeclaration of the base method in TOmniParallelSimpleLoop from non-virtual to virtual change the base type?

Tak, będzie. Zmienia tę metodę z nie wirtualnej na wirtualną. Oznacza to, że wysyłanie metod odbywa się w różny sposób, jak opisano powyżej. Oznacza to, że VMT typu zmienia się, aby dostosować się do nowej wirtualnej metody.

Or was the method already virtual to begin with (due to it being an implementation of an interface method)?

Fakt, że metoda służy do implementacji części interfejsu, nie zmienia sposobu, w jaki kompilator ją traktuje. Metoda nie-wirtualna jest zaimplementowana w taki sam sposób, niezależnie od tego, czy implementuje ona metodę interfejsu, czy nie. Podobnie w przypadku metody wirtualnej. Generowanie VMT w celu implementacji interfejsu stanowi odrębny problem.

Opracowanie na tej podstawie, każda metoda ma adres. Podczas wywoływania metody innej niż wirtualna kompilator dokładnie wie, którą metodę wywołać. Więc może emitować kod, aby wywołać tę znaną metodę bezpośrednio. W przypadku metody wirtualnej kompilator nie wie, która metoda zostanie wywołana. Jest to określone przez typ środowiska wykonawczego. Tak więc kompilator emituje kod, aby odczytać znany wpis w VMT obiektu, a następnie wywołać tę metodę.

Teraz, jak jestem pewien, wiesz, interfejsy są również realizowane za pomocą VMT. Nie oznacza to jednak, że metody wdrażania automatycznie stają się wirtualne. Interfejs to po prostu VMT. Metody przywoływane przez interfejs VMT mogą być wirtualne lub nie-wirtualne, gdy są uważane za metody klasy.

type 
    ISomeInterface = interface 
    procedure Foo; 
    end; 

    TSomeObject = class(TInterfacedObject, ISomeInterface) 
    procedure Foo; 
    end; 

.... 

var 
    Intf: ISomeInterface; 
    Obj: TSomeObject; 
.... 
Intf := TSomeObject.Create; 
Obj := Intf as TSomeObject; 

// non-virtual method, direct dispatch at compile time 
Obj.SomeMethod; 

// interface method, dispatched via interface VMT 
Intf.SomeMethod; 

Fakt, że metoda może zostać wywołana za pomocą VMT, nie oznacza, że ​​należy ją wywoływać w ten sposób.

+0

Hm, nie odpowiedziałeś na pytanie: 'Czy redeclaration metody bazowej [...] z podstawowego na wirtualny zmień obiekt bazowy'. To znaczy. dodanie słowa kluczowego 'virtual' spowoduje, że kompilator wygeneruje inny kod dla obiektu TBaseObject. – Johan

+0

"Tak, będzie." Jesteś pewny? Myślę, że tak nie będzie. (ale nie w 100% pewne). – Johan

+1

100% pewności, że będzie. Możesz sprawdzić emitowany kod. Sposób wysyłki jest zupełnie inny. VMT jest inny. –

3

Myślę, że odpowiedź na to pytanie jest zawarta w jednym zdaniu Danny Thorpe w "Delphi Komponent Design", ISBN 0-201-46136-6, wspominałem w komentarzu do użytkownika @ DavidH odpowiedź:

„A VMT jest dokładnie tablicą wskaźników funkcji ", w sekcji" Importowanie obiektów z bibliotek DLL - The Hard Way ", str. 89.

W poniższej sekcji "Importowanie obiektów z bibliotek DLL - metoda inteligentna" wyjaśniono, w jaki sposób można wykorzystać tablicę wskaźników funkcji dla członków abstrakcyjnego interfejsu, aby wywołać bibliotekę DLL, która implementuje interfejs i jak to (udar geniuszu , nie tylko imo) stanowiło podstawę wsparcia COM dla Delphi (po przejściu wsparcia za pośrednictwem wariantów D2).

+0

Książka miłości Danny'ego. –

+0

Twoje opisanie geniuszu COM, a nie cokolwiek specyficznego dla Delphi –

+0

@DavidHeffernan: Przepraszam, nie zgadzam się. Dale Rogerson's Inside COM opisuje bałagan (ale spójny) zestaw rozwiązań dla bałaganu. Nie jest to dla mnie geniuszem, aw każdym razie nie był to jedyny pokaz w mieście (c.f, Corba). Przy okazji, ten fragment, który cytowałem z D Thorpe, nie mówi o COM. – MartynA

Powiązane problemy