Arv (programmering)

Arv (eng. inheritance ) - begrebet objektorienteret programmering , ifølge hvilket en abstrakt datatype kan arve data og funktionalitet af en eksisterende type, hvilket letter genbrugen af ​​softwarekomponenter .

Terminologi

I objektorienteret programmering , siden Simula 67 , kaldes abstrakte datatyper klasser .

Superklasse ( eng.  superklasse ), forældreklasse ( eng.  overordnet klasse ), forfader, forælder eller superklasse - en klasse, der producerer arv i underklasser, altså en klasse, som andre klasser arver fra. En superklasse kan være en underklasse, en basisklasse, en abstrakt klasse og en grænseflade.

Underklasse ( eng.  underklasse ), afledt klasse ( eng.  afledt klasse ) , børneklasse ( eng. underklasse ) , efterkommerklasse, efterfølgerklasse eller implementeringsklasse - en klasse nedarvet fra en superklasse eller grænseflade, altså en klasse defineret gennem arv fra en anden klasse eller flere sådanne klasser. En underklasse kan være en superklasse.  

En  basisklasse er en klasse , der er øverst i klassearvshierarkiet og nederst i underklassetræet, det vil sige, at den ikke er en underklasse og ikke arver fra andre superklasser eller grænseflader. Basisklassen kan være en abstrakt klasse og en grænseflade. Enhver ikke-basisklasse er en underklasse.

En  grænseflade er en struktur, der definerer en ren klassegrænseflade bestående af abstrakte metoder. Interfaces deltager i arvehierarkiet af klasser og grænseflader.

En supergrænseflade ( eng.  supergrænseflade ) eller en forfædregrænseflade er en analog af en superklasse i arvshierarkiet, det vil sige, at det er en grænseflade, der arver i underklasser og undergrænseflader.

En efterkommergrænseflade, afledt grænseflade eller afledt  grænseflade er en analog af en underklasse i nedarvningshierarkiet af grænseflader, dvs. det er en grænseflade nedarvet fra en eller flere supergrænseflader.

En basisgrænseflade svarer til en basisklasse i nedarvehierarkiet af grænseflader, dvs. det er grænsefladen øverst i nedarvehierarkiet.

Et arvehierarki eller klassehierarki er et træ, hvis elementer er klasser og grænseflader.

Ansøgning

Arv er en mekanisme til genbrug af kode (engelsk kode genbrug ) og bidrager til uafhængig udvidelse af software gennem åbne klasser (engelske offentlige klasser) og grænseflader (engelske grænseflader). Indstilling af et arveforhold mellem klasser genererer et klassehierarki.

Arv og subtype polymorfi

Arv identificeres ofte med subtype polymorfi :

På trods af ovenstående bemærkning er arv en meget brugt mekanisme til at etablere et er -et forhold. Nogle programmeringssprog er enige om arv og subtype polymorfi (for det meste statisk indtastede sprog som C++C#Java og Scala ), mens andre deler ovenstående begreber.

Nedarvning – selv i programmeringssprog, der understøtter brugen af ​​arv som en mekanisme for subtype polymorfi – garanterer ikke subtype adfærdspolymorfi; se: "Substitutionsprincippet" af Barbara Liskov .

Arvetyper

"Simpel" arv

"Simpel" arv, nogle gange kaldet enkelt arv, beskriver forholdet mellem to klasser, hvoraf den ene arver den anden. Mange klasser kan stamme fra en enkelt klasse, men alligevel forbliver denne form for forhold "simpel" arv.

Abstrakte klasser og oprettelse af objekter

For nogle programmeringssprog er følgende koncept gyldigt.

Der er "abstrakte" klasser (erklæret som sådanne vilkårligt eller på grund af de abstrakte metoder , der er tildelt dem ); de kan beskrives som havende felter og metoder . Oprettelsen af ​​objekter (instanser) betyder konkretisering , der kun gælder for ikke-abstrakte klasser (herunder ikke-abstrakte efterkommere af abstrakte), hvis repræsentanter som et resultat vil være de skabte objekter.

Eksempel: Lad basisklassen "Ansat ved Universitetet ", hvorfra klasserne " Postgraduate student " og " Professor " er nedarvet, være abstrakt. Klassers almindelige felter og funktioner (f.eks. feltet "Fødselsår") kan beskrives i basisklassen. Og programmet vil oprette objekter af kun afledte klasser: "Postgraduate student" og "Professor"; det giver normalt ikke mening at oprette objekter af basisklasser.

Multipel arv

Med multipel arv kan en klasse have mere end én forælder. I dette tilfælde arver klassen alle forfædres metoder . Fordelen ved denne tilgang er større fleksibilitet.

Multipel nedarvning er implementeret i C++ . Andre sprog, der giver denne funktion, inkluderer Python og Eiffel . Multipel nedarvning er understøttet i UML .

Multipel arv er en potentiel kilde til fejl, der kan opstå ved at have de samme metodenavne i forfædre. I sprog, der er positioneret som efterfølgere af C++ ( Java , C# og andre), blev det besluttet at opgive multipel arv til fordel for grænseflader . Du kan næsten altid undvære denne mekanisme. Men hvis et sådant behov alligevel opstod, så for at løse konflikter i brugen af ​​nedarvede metoder med de samme navne, er det for eksempel muligt at anvende synlighedsudvidelsesoperationen - "::" - at kalde en specifik metode for en specifik forælder.

Et forsøg på at løse problemet med at have de samme metodenavne i forfædre blev lavet på Eiffel -sproget , hvor det, når man beskriver en ny klasse, er nødvendigt eksplicit at angive de importerede medlemmer af hver af de nedarvede klasser og deres navngivning i børneklasse.

De fleste moderne objektorienterede programmeringssprog ( C# , Java , Delphi og andre) understøtter muligheden for samtidigt at arve fra en forfaderklasse og implementere metoder til flere grænseflader af samme klasse. Denne mekanisme giver dig mulighed for stort set at erstatte flere arv - grænseflademetoder skal eksplicit omdefineres, hvilket eliminerer fejl, når du nedarver funktionaliteten af ​​de samme metoder fra forskellige forfaderklasser.

Enkelt basisklasse

I en række programmeringssprog arver alle klasser, enten eksplicit eller implicit, fra en eller anden basisklasse. Smalltalk var et af de første sprog, der brugte dette koncept. Disse sprog inkluderer også: Objective-C (klasse NSObject), Perl ( UNIVERSAL), Eiffel ( ANY), Java ( java.lang.Object), C# ( System.Object), Delphi ( TObject), Scala ( Any).

Arv i programmeringssprog

C++

Nedarvning i C++ :

klasseA { }; // Basisklasse klasse B : offentlig A {}; // Offentlig arv klasse C : beskyttet A {}; // Beskyttet arveklasse Z : privat A { }; // Privat arv

Der er tre typer af arv i C++ : offentlig , beskyttet , privat . Adgangsspecifikationer for basisklassemedlemmer ændres i efterkommere som følger:

  • Hvis en klasse er erklæret som basisklassen for en anden klasse med en adgangsspecifikation...
    • ... offentligt :
      • offentlige medlemmer af basisklassen - tilgængelige som offentlige medlemmer af den afledte klasse;
      • beskyttede medlemmer af basisklassen - tilgængelige som beskyttede medlemmer af den afledte klasse;
    • … beskyttet :
      • offentlige og beskyttede medlemmer af basisklassen er tilgængelige som beskyttede medlemmer af den afledte klasse;
    • … privat :
      • offentlige og beskyttede medlemmer af basisklassen er tilgængelige som private medlemmer af den afledte klasse.

En af de vigtigste fordele ved offentlig arv er, at en pointer til afledte klasser implicit kan konverteres til en pointer til basisklassen, så for eksemplet ovenfor kan du skrive:

A * a = nyB ( );

Denne interessante funktion åbner muligheden for dynamisk typeidentifikation (RTTI).

Delphi (Objekt Pascal)

For at bruge arvemekanismen i Delphiclass skal du angive stamfaderklassen i klasseerklæringen i parentes :

Forfader:

TAncestor = klasse privat beskyttet offentlig // Virtuel procedure procedure VirtualProcedure ; virtuel ; abstrakt ; procedure StaticProcedure ; ende ;

Arving:

TDescendant = klasse ( TAncestor ) privat beskyttet offentlig // Virtuel procedure tilsidesættelsesprocedure VirtualProcedure ; tilsidesætte ; procedure StaticProcedure ; ende ;

Absolut alle klasser i Delphi er efterkommere af TObject. Hvis en forfaderklasse ikke er specificeret, antages den nye klasse at være en direkte efterkommer af TObject.

Multipel arv i Delphi er i udgangspunktet ikke understøttet i princippet, men for dem, der ikke kan undvære det, er der stadig sådanne muligheder, for eksempel gennem brug af hjælperklasser (Сlass Helpers).

Python

Python understøtter både enkelt og multipel nedarvning. Når du får adgang til en attribut, sker visning af afledte klasser i rækkefølgen efter metodeopløsningsrækkefølge  (MRO ) [1] .

klasse Ancestor1 ( objekt ): # Ancestor-1 def m1 ( selv ): bestå klasse Ancestor2 ( objekt ): # Ancestor-2 def m1 ( self ): bestå klasse Descendant ( Ancestor1 , Ancestor2 ): # Descendant def m2 ( self ): passere d = Efterkommer () # Forekomst print d . __klasse__ . __mro__ # Metodeopløsningsrækkefølge: ( < klasse ' __main__ . Descendant '>, <klasse ' __main__ . Ancestor1 '>, <klasse ' __main__ . Ancestor2 '>, <type ' objekt '>)

Fra Python 2.2 eksisterer "klassiske" klasser og "nye" klasser side om side i sproget. Sidstnævnte er arvinger object. "Klassiske" klasser vil blive understøttet op til version 2.6, men fjernet fra sproget i Python 3.0.

Multipel nedarvning bruges i Python, især for at introducere mix-in klasser i hovedklassen . 

PHP

For at bruge arvemekanismen i PHPextends er det nødvendigt at angive ordet og navnet på stamfaderklassen efter navnet på den erklærede efterfølgerklasse i klasseerklæringen :

klasse Descendant udvider Ancestor { }

Hvis den afledte klasse overlapper forfadermetoderne, kan forfadermetoderne tilgås ved hjælp af parent:

klasse A { funktionseksempel () { echo "Metode A:: eksempel () kaldet.<br /> \n " ; } } klasse B udvider A { funktionseksempel () { echo "Method B::example() kaldet.<br /> \ n " ; forælder :: eksempel (); } }

Det er muligt at forhindre en afledt klasse i at tilsidesætte en forfaders metoder; for at gøre dette skal du angive nøgleordet final:

klasse A { final function example () { echo "Method A::example() kaldet.<br /> \n " ; } } klasse B udvider A { funktionseksempel () { //vil kaste en fejlforælder :: eksempel ( ); //og vil aldrig udføres } }

For at referere til konstruktøren af ​​overordnet klasse under nedarvning, er det nødvendigt for den underordnede klasse at specificere i konstruktøren parent::__construct();[2]

Objective-C

@interface A  : NSObject- ( void ) eksempel ; _ @ende @implementation - ( ugyldigt ) eksempel { NSLog ( @"ClassA" ); } @ende @interface B  : A - ( ugyldigt ) eksempel ; @ende @implementation - ( ugyldigt ) eksempel { NSLog ( @"KlasseB" ); } @ende

Interfacet erklærer metoder, der vil være synlige uden for klassen (public).

Interne metoder kan implementeres uden en grænseflade. For at erklære yderligere egenskaber skal du bruge interface-udvidelse i implementeringsfilen.

Alle metoder i Objective-C er virtuelle.

Java

Et eksempel på arv fra én klasse og to grænseflader :

offentlig klasse A { } offentlig grænseflade I1 { } offentlig grænseflade I2 { } offentlig klasse B udvider A implementerer I1 , I2 { }

Et direktiv finali en klasseerklæring gør det umuligt at arve fra det.

C#

Et eksempel på arv fra én klasse og to grænseflader :

public class A { } public interface I1 { } public interface I2 { } public class B : A , I1 , I2 { }

Nedarvning fra indtastede klasser kan ske ved at angive en fast type eller ved at overføre en typevariabel til en nedarvet klasse:

offentlig klasse A < T > { } offentlig klasse B : A < int > { } offentlig klasse B2 < T > : A < T > { }

Det er også muligt at arve indlejrede klasser fra klasser, der indeholder dem:

klasse A // standard klasse A er intern, ikke offentlig klasse B kan ikke være offentlig { klasse B : A { } }

Et direktiv sealedi en klasseerklæring gør det umuligt at arve fra det. [3]

Ruby

klasseforælder _ def public_method "Offentlig metode" ende privat def private_method "Privat metode" ende ende klasseBarn < Forælder _ def public_method "Omdefineret offentlig metode" ende def call_private_method "Ancestors private metode: " + private_method end ende

Klassen Parenter forfaderen til den klasse, Childhvis metode er tilsidesat public_method.

barn = Barn . nybarn . _ public_method #=> "Omdefineret offentlig metode" underordnet . call_private_method #=> "Ancestors private metode: Privat metode"

En forfaders private metoder kan kaldes fra efterkommere.

JavaScript

klasse Forælder { konstruktør ( data ) { dette . data = data ; } publicMethod () { return 'Offentlig metode' ; } } klasse Child udvider Parent { getData () { return `Data: ${ this . data } ` ; } publicMethod () { return 'Omdefineret offentlig metode' ; } } const test = newChild ( ' test' ); test . getdata (); // => 'Data: test' test . publicMethod (); // => 'Omdefineret offentlig metode' test . data ; // => 'test'

Klassen Parenter forfaderen til den klasse, Childhvis metode er tilsidesat publicMethod.

JavaScript bruger prototypisk arv.

Konstruktører og destruktorer

I C++ kaldes konstruktører sekventielt under nedarvning fra den tidligste forfader til det seneste barn, og omvendt kaldes destruktorer fra det seneste barn til den tidligste forfader.

klasseFørst _ { offentligt : First () { cout << ">>Første konstruktør" << endl ; } ~ First () { cout << ">>First destructor" << endl ; } }; klasse Anden : offentlig først { offentligt : Anden () { cout << ">Anden konstruktør" << endl ; } ~ Anden () { cout << ">Second destructor" << endl ; } }; klasse Tredje : offentlig Anden { offentligt : Third () { cout << "Third constructor" << endl ; } ~ Third () { cout << "Third destructor" << endl ; } }; // kodeudførelse Third * th = new Third (); slette th ; // output resultat /* >>Første konstruktør >Anden konstruktør Tredje konstruktør Tredje destruktor >Anden destruktor >>Første destruktor */

Links

Noter

  1. om metodeopløsningsrækkefølge i Python
  2. Hvad er objektorienteret programmering . wh-db.com (30. juni 2015).
  3. C# sprogspecifikation version 4.0, Copyright © Microsoft Corporation 1999-2010