Konstruktør (objektorienteret programmering)

Den aktuelle version af siden er endnu ikke blevet gennemgået af erfarne bidragydere og kan afvige væsentligt fra den version , der blev gennemgået den 28. juni 2016; checks kræver 22 redigeringer .

I objektorienteret programmering er en klassekonstruktør (fra den engelske constructor  ) en speciel blok af instruktioner kaldet når et objekt oprettes.

Konstruktøropgave

En af nøglefunktionerne ved OOP er indkapsling : Klassens interne felter er ikke direkte tilgængelige, og brugeren kan kun arbejde med objektet som helhed gennem offentlige ( public) metoder. Hver metode bør ideelt set designes således, at et objekt, der er i en "gyldig" tilstand (det vil sige, når klassen invariant er opfyldt ) også er i en gyldig tilstand, når metoden påkaldes. Og konstruktørens første opgave er at overføre objektets felter til en sådan tilstand.

Den anden opgave er at forenkle brugen af ​​objektet. Et objekt er ikke en " ting i sig selv ", det skal ofte kræve noget information fra andre objekter: for eksempel skal et objekt File, når det oprettes, modtage et filnavn. Dette kan også gøres via metoden:

fil fil ; fil . open ( "in.txt" , Fil :: omRead );

Men det er mere praktisk at åbne filen i konstruktøren: [1]

Fil fil ( "in.txt" , Fil :: omRead );

Typer af konstruktører

En række programmeringssprog præsenterer flere varianter af konstruktører:

  • konstruktør med parametre;
  • standardkonstruktør , der ikke tager nogen argumenter;
  • navngivet konstruktør - en funktion, der antager et eksplicit kald ved navn, der fungerer som en konstruktør
  • kopikonstruktør  - en konstruktør, der tager som argument et objekt af samme klasse (eller en reference fra det);
  • konverteringskonstruktør - en konstruktør, der tager et argument (disse konstruktører kan kaldes automatisk for at konvertere værdier af andre typer til objekter af denne klasse).
  • flytte konstruktør ( C++11 specifik )
klasse Kompleks { offentligt : // Standardkonstruktør // (i dette tilfælde også en konverteringskonstruktør) Kompleks ( double i_re = 0 , double i_im = 0 ) : re ( i_re ), im ( i_im ) {} // Kompleks kopi konstruktør ( const Complex & obj ) { re = obj . re ; im = obj . im ; } privat : dobbelt re , im ; };

Konstruktør med parametre

Konstruktører, der tager et eller flere argumenter, kaldes parametriserede. For eksempel:

klasse eksempel { int x , y ; offentligt : eksempel (); Eksempel ( int a , int b ); // parameteriseret konstruktør }; Eksempel :: Eksempel () { } Eksempel :: Eksempel ( int a , int b ) { x = a ; y = b ; }

En parameteriseret konstruktør kan kaldes eksplicit eller implicit, for eksempel:

Eksempel e = Eksempel ( 0 , 50 ); // eksplicit opkald Eksempel e ( 0 , 50 ); // implicit opkald

Standard konstruktør

En konstruktør uden påkrævede argumenter. Bruges ved oprettelse af arrays af objekter, kaldet til at oprette hver instans. I mangel af en eksplicit standardkonstruktør genereres dens kode af compileren (hvilket selvfølgelig ikke afspejles i kildeteksten).

Navngivet konstruktør

Kopier konstruktør

En konstruktør, hvis argument er en reference til et objekt af samme klasse. Bruges i C++ til at sende objekter til funktioner efter værdi .

Kopikonstruktøren er for det meste nødvendig, når et objekt har pointere til objekter, der er allokeret på heapen . Hvis programmøren ikke opretter en kopikonstruktør, vil compileren oprette en implicit kopikonstruktør, der kopierer pointerne som de er , dvs. der sker ingen egentlig kopiering af dataene, og de to objekter refererer til de samme data på heapen. Følgelig vil et forsøg på at ændre "kopien" beskadige originalen, og at kalde destruktoren for et af disse objekter, med efterfølgende brug af det andet, vil føre til adgang til et hukommelsesområde, der ikke længere hører til programmet.

Argumentet skal sendes ved reference , ikke efter værdi . Dette følger af en kollision: når du sender et objekt efter værdi (især for at kalde en konstruktør), er det nødvendigt at kopiere objektet. Men for at kopiere et objekt skal du kalde kopikonstruktøren.

Konverteringskonstruktør

En konstruktør, der tager ét argument. Specificerer typekonverteringen af ​​dets argument til typen af ​​konstruktøren. Denne typekonvertering anvendes kun implicit, hvis den er unik.

En brugerdefineret typekonvertering kan have en af ​​to former: - fra en klassetype C til en hvilken som helst type T, for hvilken C skal have en C::operator T() - fra en hvilken som helst type T til en klassetype C, for hvilken C skal have C::C(T) (eller C::C(T&), eller C::C(T&&))

Hvis begge disse tilfælde tillades i et udtryk, opstår der en flertydighed og en kompileringsfejl.

Hvis en konstruktør (eller operator T()) er markeret med det eksplicitte nøgleord, anvendes en sådan typekonvertering kun, hvis der er en eksplicit cast-operation af formen (T)C eller static_cast<T>C. Hvis der ikke er noget eksplicit ord, så kan compileren indsætte en sådan konvertering selv implicit, for eksempel når funktionen f(T arg) kaldes i formen f(C).

Flyttekonstruktøren

C ++11 introducerer en ny type ikke-konstante referencer kaldet rvalue  reference og betegnet som T&&, og en ny slags konstruktør - flyt konstruktører .  Flyttekonstruktøren tager værdien af ​​en ikke-konstant reference til et klasseobjekt som input og bruges til at overføre ejerskab af det objekts ressourcer. Move-konstruktører blev opfundet for at løse effektivitetstabet forbundet med at skabe midlertidige objekter.

Virtuel konstruktør

En konstruktør er ikke virtuel i betydningen en virtuel metode  - for at den virtuelle metode-mekanisme skal fungere, skal du køre konstruktøren, som automatisk opsætter den virtuelle metodetabel for dette objekt.

"Virtuelle konstruktører" refererer til en lignende, men anderledes mekanisme, der findes i nogle sprog, såsom Delphi , men ikke C++ og Java . Denne mekanisme giver dig mulighed for at oprette et objekt af enhver tidligere ukendt klasse under to betingelser:

  • denne klasse er en efterkommer af en foruddefineret klasse (i dette eksempel er det en klasse TVehicle);
  • på hele arvestien fra basisklassen til den oprettede, brød redefinitionskæden ikke. Ved tilsidesættelse af en virtuel metode kræver Delphi-syntaksen nøgleordet overload, så gamle og nye funktioner med forskellige signaturer kan eksistere side om side, overrideenten for at tilsidesætte funktionen eller reintroducefor at definere en ny funktion med samme navn - sidstnævnte er ikke tilladt.
type TVehicle = klassekonstruktør Opret ; _ virtuel ; ende ; TAutomobile = klasse ( TVehicle ) constructor Opret ; tilsidesætte ; ende ; TMotorcykel = klasse ( TVehicle ) constructor Opret ; tilsidesætte ; ende ; TMoped = klasse ( TMotorcykel ) // bryd omdefineringskæden - start en ny Opret konstruktør Opret ( x : heltal ) ; genindføre ; ende ;

Den såkaldte klassetype ( metaklasse ) introduceres i sproget. Denne type kan tage navnet på enhver klasse afledt af som værdi TVehicle.

type CVehicle = klasse af TVehicle ;

Denne mekanisme giver dig mulighed for at oprette objekter af enhver tidligere ukendt klasse afledt af TVehicle.

var cv : CVehicle ; v : TV-køretøj ; cv := TAutomobil ; v := cv . skabe ;

Bemærk, at koden

cv := TMoped ; v := cv . skabe ;

er forkert - direktivet har reintroducebrudt kæden med at tilsidesætte den virtuelle metode, og faktisk vil konstruktøren blive kaldt TMotorcycle.Create(hvilket betyder, at der bliver skabt en motorcykel, ikke en knallert!)

Se også Fabrik (designmønster)

Syntaks

C++

Navnet på konstruktøren skal svare til navnet på klassen. Flere konstruktører med samme navn, men forskellige parametre er tilladt .

Eksempel klasse ClassWithConstructor { offentligt : /* Initialiser internt objekt med konstruktør */ ClassWithConstructor ( float parameter ) : objekt ( parameter ) {} /* kalder constructor AnotherClass(float); */ privat : AnotherClass objekt ; };

Python

I Python er en konstruktør en klassemetode ved navn __init__. Glem heller ikke, at det første argument til enhver metode skal være en pegepind til klassens kontekst self.

Eksempel klasse ClassWithConstructor : def __init__ ( self ): """Denne metode er konstruktør.""" pass

Ruby

Ruby - sproget bruger en speciel metode til at sætte et objekt til dets oprindelige konsistente tilstand initialize.

Eksempel klasse ClassWithConstructor def initialize print 'Denne metode er konstruktør.' ende ende

Delphi

I Delphi , i modsætning til C++ , er konstruktøren erklæret med nøgleordet constructor. Navnet på konstruktøren kan være hvad som helst, men det anbefales at navngive konstruktøren Create.

Eksempel TClassWithConstructor = klasse offentlig konstruktør Opret ; ende ;

Java

Nogle forskelle mellem konstruktører og andre Java- metoder :

  • konstruktører har ikke en returtype (faktisk returnerer de altid denne);
  • konstruktører kan ikke kaldes direkte (nøgleordet skal bruges new);
  • konstruktører kan ikke have synchronized, final, abstract, nativeog modifikatorer static;
Eksempel public class Eksempel { private int data ; // Standard konstruktør, data initialiseres til 1, når en instans af klassen Eksempel oprettes offentligt Eksempel () { data = 1 ; } // Konstruktør overbelastning offentlig Eksempel ( int input ) { data = input ; } } // kode, der illustrerer skabelsen af ​​et objekt af konstruktøren beskrevet ovenfor. Eksempel e = nyt eksempel ( 42 );

JavaScript

I JavaScript er konstruktøren en almindelig funktion, der bruges som operand for operatoren new. Nøgleordet bruges til at henvise til det oprettede objekt this.

Imidlertid tilføjede ECMAScript 6-specifikationen en prototype syntaktisk wrapper, som har sådanne OOP- egenskaber som arv, samt en lille liste over nødvendige metoder, for eksempel: toString().

Eksempel funktion Eksempel ( initValue ) { this . minVærdi = begyndelsesVærdi ; } eksempel . prototype . getMyValue = funktion () { returner dette . minVærdi ; } //ES6 klasse klasse Eksempel { constructor () { console . log ( 'konstruktør' ); } } // kode, der illustrerer oprettelsen af ​​et objekt af konstruktøren beskrevet ovenfor var exampleObject = new Eksempel ( 120 );

Visual Basic .NET

Konstruktører i Visual Basic .NET bruger en almindelig deklarationsmetode ved navn New.

Eksempel Klasse Foobar Private strData Som streng ' Constructor Public Sub New ( ByVal someParam As String ) strData = someParam End Sub End Class 'noget kode ', der illustrerer oprettelsen af ​​et objekt af Dim foo As New Foobar ( ".NET" ) konstruktøren ovenfor

C#

Eksempel klasse MyClass { privat int _nummer ; privat streng _streng ; public MyClass ( int num , string str ) { _number = num ; _streng = str ; } } // Kode, der illustrerer oprettelsen af ​​et objekt af konstruktøren beskrevet ovenfor . MyClass- eksempel = new MyClass ( 42 , "string" );

Eiffel

I Eiffel kaldes rutiner, der initialiserer objekter, oprettelsesprocedurer . Oprettelsesprocedurer ligner en del konstruktører og noget anderledes. De har følgende egenskaber:

  • Oprettelsesprocedurer har ikke nogen eksplicit returresultattype (som defineret af procedure [Note 1] ).
  • oprettelsesprocedurer navngives (navne er begrænset til gyldige identifikatorer);
  • oprettelsesprocedurer er specificeret ved navne i klasseteksten;
  • oprettelsesprocedurer kan kaldes direkte (som normale procedurer) for at geninitialisere objekter;
  • hver effektiv (det vil sige konkret, ikke abstrakt) klasse skal (eksplicit eller implicit) specificere mindst én oprettelsesprocedure;
  • oprettelsesprocedurer er ansvarlige for at bringe det nyligt initialiserede objekt i en tilstand, der tilfredsstiller klasseinvarianten [Note 2] .

Selvom oprettelse af objekter er genstand for nogle finesser [Note 3] , består oprettelse af en attribut med en typeerklæring x: Tudtrykt som en oprettelseserklæring create x.makeaf følgende sekvens af trin:

  • oprette en ny direkte instans af typen T[Note 4] ;
  • udføre oprettelsesproceduren makefor den nyoprettede instans;
  • vedhæfte det nyoprettede objekt til objektet x.
Eksempel

Den første passage nedenfor definerer klassen POINT. Proceduren makeer kodet efter nøgleordet feature.

Nøgleordet createintroducerer en liste over procedurer, der kan bruges til at initialisere forekomster af klassen. I dette tilfælde indeholder listen default_create, en procedure med en tom implementering, der er arvet fra klassen ANY, og en procedure makemed en implementering i selve klassen POINT.

klasse POINT oprette default_create , make funktion make ( a_x_value : REAL ; a_y_value : REAL ) do x := a_x_value y := a_y_value end x : REAL -- X-koordinat y : RIGTIG -- Y koordinat ...

I den anden passage har klassen, der er klienten til klassen POINT, erklæringer my_point_1af my_point_2typen POINT.

I subrutinekoden my_point_1oprettes den med koordinater (0.0; 0.0). Da der ikke er angivet nogen oprettelsesprocedure i oprettelsessætningen, bruges proceduren, der er default_createarvet fra klassen ANY. Den samme linje kunne omskrives som create my_point_1.default_create. Kun procedurer, der er angivet som oprette-procedurer, kan bruges i oprette-sætninger (det vil sige sætninger med nøgleordet create).

Dernæst kommer opret-instruktionen for my_point_2, som sætter startværdierne for koordinaterne my_point_2.

Den tredje instruktion foretager et normalt procedurekald for makeat geninitialisere den instans, der er knyttet til, my_point_2med forskellige værdier.

mit_punkt_1 : PUNKT mit_punkt_2 : PUNKT ... opret mit_punkt_1 opret mit_punkt_2 . make ( 3.0 , 4.0 ) mit_punkt_2 . make ( 5.0 , 8.0 ) ...

Cold Fusion

Eksempel

Det skal bemærkes, at der ikke er nogen konstruktørmetode i ColdFusion . En almindelig metode blandt ColdFusion-programmeringsfællesskabet er at kalde ' '-metoden initsom en pseudo-konstruktør.

<cfcomponent displayname = "Ost" > <!--- egenskaber ---> <cfset variabler . cheeseName = "" / > <!--- pseudo-constructor ---> <cffunction name = "init" returntype = "Cheese" > <cfargument name = "cheeseName" type = "string" required = "true" / > <cfset variabler . cheeseName = argumenter . cheeseName / > <cfreturn this / > </cffunction> </cfcomponent>

PHP

Eksempel

I PHP (siden version 5) er en konstruktør en metode __construct(), der automatisk kaldes af et nøgleord, newefter at et objekt er blevet oprettet. Anvendes typisk til at udføre forskellige automatiske initialiseringer, såsom egenskabsinitialisering. Konstruktører kan også tage argumenter, i hvilket tilfælde, når et udtryk er angivet new, skal formelle parametre sendes til konstruktøren i parentes.

klasse Person { privat $navn ; funktion __construct ( $navn ) { $this -> navn = $navn ; } function getName () { return $this -> name ; } }

En konstruktør i PHP version 4 (og tidligere) er dog en klassemetode med samme klassenavn.

klasse Person { privat $navn ; function Person ( $navn ) { $this -> navn = $navn ; } function getName () { return $this -> name ; } }

Perl

Eksempel

I Perl skal konstruktøren anvende velsignefunktionen på en eller anden variabel (normalt en hash-reference):

pakke Eksempel ; sub new { min $klasse = skift ; mit $selv = {}; retur velsigne $selv , $klasse ; } 1 ;

Men dette er den mindste grundlæggende mulighed, der er mange mere avancerede metoder, lige fra brugsfelter til Moose.

Forenklede konstruktører (med pseudokode )

Konstruktører er altid en del af implementeringen af ​​klasser. En klasse (i programmering) beskriver specifikationerne for de grundlæggende egenskaber for det sæt af objekter, der er medlemmer af klassen, ikke de individuelle karakteristika for nogen af ​​objekterne. Lad os se på en simpel analogi. Tag som eksempel et sæt (eller klasse, for at bruge dets mere generelle betydning) af elever fra en bestemt skole. Vi har således:

klasse elev { // beskrivelse af elevklassen // ... anden kode ... }

Klassen Student er dog blot en generel skabelon (prototype) for vores elever. For at bruge det opretter programmøren hver elev som et objekt eller en enhed ( implementering ) af klassen. Dette objekt er det rigtige stykke data i hukommelsen, hvis størrelse, mønster, karakteristika og (til en vis grad) adfærd er defineret af klassedefinitionen. Den sædvanlige måde at oprette objekter på er at kalde en konstruktør (klasser kan generelt have separate konstruktører). For eksempel,

klasse elev { Student(String studentName, String Address, int ID) { // ... her gemmer vi inputdata og andre interne felter ... } //... }

Se også

Noter

  1. Eiffel -underrutiner er enten procedurer eller funktioner . Procedurer har ingen returtype. Funktioner har altid en returtype.
  2. Da invarianten af ​​den eller de nedarvede klasse(r) også skal være opfyldt, er der intet obligatorisk krav om at kalde overordnede konstruktører.
  3. Den fulde specifikation er indeholdt i ISO/ECMA-standarderne for programmeringssproget Eiffel, tilgængelig online. [2]
  4. Eiffel-standarden kræver, at felter initialiseres, første gang de tilgås, inkl. der er ingen grund til at initialisere dem med standardværdier på tidspunktet for oprettelse af objekter.

Links

  1. Dette fører selvfølgelig til visse tekniske vanskeligheder - for eksempel, hvad sker der, hvis en undtagelse bliver smidt fra konstruktøren ? Klasseudvikleren skal dog blot overholde sprogets krav, og de fleste programmer kræver ikke detaljeret diagnostik og automatiske genforsøg på fejl .
  2. ISO/ECMA Eiffel beskrivelsesdokument . Hentet 19. april 2009. Arkiveret fra originalen 16. juni 2008.