Musisz rozróżnić dwie rzeczy - pierwsza to tworzenie instancji klasy, a druga to pobieranie referencji w parametrze.
Jeśli chcesz utworzyć obiekt w pamięci to nie powinieneś używać klasy abstrakcyjnej, bo taka z definicji jest niekompletna, po części niezdefiniowana. Nie wiem jak w Delphi, ale pod Lazarusem stworzenie instancji klasy abstrakcyjnej powoduje wyświetlenie szeregu ostrzeżeń związanych z jej niekompletnością. Dla przykładu weźmy bazową klasę TStrings
:
with TStrings.Create() do
try
finally
Free();
end;
Kompilacja powyższego kodu spowoduje wyrzucenie ostrzeżeń o metodach abstrakcyjnych:
project1.lpr(9,26) Warning: Constructing a class "TStrings" with abstract method "Get"
project1.lpr(9,26) Warning: Constructing a class "TStrings" with abstract method "GetCount"
project1.lpr(9,26) Warning: Constructing a class "TStrings" with abstract method "Clear"
project1.lpr(9,26) Warning: Constructing a class "TStrings" with abstract method "Delete"
project1.lpr(9,26) Warning: Constructing a class "TStrings" with abstract method "Insert"
To są ostrzeżenia, a nie błędy, więc kod można uruchomić. Problem w tym, że z takiego obiektu nie będzie się dało normalnie skorzytać. Np. metoda Insert
używana jest zawsze do dodania nowej pozycji do listy (metoda Add
z niej korzysta), więc takiej listy nie będzie można nawet uzupełnić. Próba dodania pozycji skończy się wyjątkiem - w tym przypadku będzie to RunError(211)
, czyli błąd Call to abstract method
.
Podobnie jest z klasą TGraphic
- też zawiera mnóstwo metod abstrakcyjnych i bezpośrednie skorzystanie z instancji takiej klasy (nie wliczając konstruktora i destruktora) zakończy się wyjątkiem. Dlatego też powinieneś sobie wybrać klasę, która z tej bazowej dziedziczy i która to posiada nadpisane wszystkie abstrakcyjne metody. Jeśli TGifImage
dziedziczy po TGraphic
to z tej klasy skorzystaj - o ile masz zamiar operować na grafikach w formacie GIF
.
Druga sprawa to pobieranie instancji klasy w parametrze. Metoda może posiadać parametr typu klasy abstrakcyjnej (bazowej), dlatego że klasa abstrakcyjna posiada informacje o metodach, które klasy dziedziczące będą miały zdefiniowane.
Dlatego też jeśli metoda posiada parametr typu TGraphic
:
public
procedure DoStuff(AGraphic: TGraphic);
można do niej przekazać instancję dowolnej klasy z niej dziedziczącej. W ten sposób działa metoda Canvas.Draw
- w trzecim parametrze pobiera grafikę do namalowania na płótnie. Możesz przekazać w nim dowolną grafikę, którą opisuje klasa dziedzicząca z TGraphic
, czyli np. TBitmap
, TPortableNetworkGraphic
, TGIFImage
, TTiffImage
, TJPEGImage
(nazwy tych klas wziąłem z Lazarusa).
Samo dziedziczenie skonkretyzowanej klasy nie musi być bezpośrednie - ważne, aby w ogóle zachodziło. Np. klasa TTiffImage
nie dziedziczy bezpośrednio z TGraphic
, bo ścieżka dziedziczenia wygląda tak:
TGraphic -> TRasterImage -> TCustomBitmap -> TFPImageBitmap -> TTiffImage
Wewnątrz metody posiadającej parametr typu TGraphic
, będziesz miał dostęp do wszystkiego co ta klasa zawiera, i równocześnie nie będziesz miał dostępu do elementów zawartych w klasie TTiffImage
, które w bazowej nie są określone. I o to właśnie chodzi - używając klasy abstrakcyjnej w parametrze, ogranicza się działania metody tylko do współnej funkcjonalności. Jeśli interesuje Cię specyficzna dla danej klasy funkcjonalność, która w bazowej klasie nie istnieje, to siłą rzeczy parametr nie może być typu abstrakcyjnego.
Mam nadzieję, że nie namieszałem aż tak bardzo i że całość jest w miarę zrozumiała :]