Klasser. Konstruktion av en enskild, icke-trivial klass

Relevanta dokument
Operatoröverlagring. endast operatorsymboler definierade i C++ kan överlagras = += -= *= /= %= ^= &= = <<= >>= < > <= >= ==!= && > ->*, [ ] ( )

Klasser. Klassen String funktionalitet. Klassen String funktionalitet, forts. Klassen String funktionalitet, forts. Vad ska man kunna göra?

Klassmedlemmar. Klasser. Konstruktion av en enskild, icke-trivial klass. Klassen String funktionalitet

TDDC76 PoD OH Föreläsning C Klasser

TDDC76 - Programmering och Datastrukturer

Skapa, kopiera och destruera klassobjekt

Trädtraversering. Binärt träd. Trädtraversering djupet-först. Trädtraversering bredden-först

Operatoröverlagring. 1 Definition och deklaration av operatorfunktioner. 2 Anrop av operatorfunktioner

Lektionsuppgifter. TDDI14 Objektorienterad programmering. Lektionsplanering Lektion Lektion Lektion

TDIU01 Programmering i C++

Innehåll. Pekaren this Självreferens. Klasser Resurshantering, representation. Överlagring av operatorer. Överlagring av operatorer

Klasser. Kapitel 2. Kapitel 2 - Klasser, medlemmar och arv. Klasser. Klasser Medlemmar Arv

Innehåll. Användardefinierade typer. Användardefinierade typer Kategorier. Konstruktorer. Konstruktorer Två sätt att skriva initiering av medlemmar

Programmering i C++ EDA623 Objektorienterad programutveckling. EDA623 (Föreläsning 5) HT / 33

Programmering i C++ EDA623 Mer om klasser. EDA623 (Föreläsning 6) HT / 26

Innehåll. Pekare Exempel

Programsystem konstruktion med C++ (2D1387) Innehåll. övning 2 klasser och arv

Introduktion. Klasser. TDP004 Objektorienterad Programmering Fö 2 Objektorientering grunder

Programsystemkonstruktion med C++: Övning 2. Karl Palmskog september 2010

Innehåll. Pekare Exempel

TDIU20 - Objektorienterad programmering i c++ - föreläsning 4

Del2 Klasser, medlemmar och arv Ämnesområden denna föreläsning:

Dynamisk bindning och polymorfism

Exempelvis pekarvariabler blir då lättare korrekt deklarerade och initierade.

Innehåll. 1 Kort om dynamisk polymorfism. 2 Arv i C++ 3 Multipelt arv. 4 Något om statisk polymorfism. class Container {

Innehåll. Konstruktorer vid arv Regler för basklassens konstruktor. Konstruktorer vid arv. Konstruktorer vid arv. Konstruktorer vid arv

TDIU01 - Programmering i C++, grundkurs

Kapitel 6 - Undantag

KLASSER. Inkapsling Abstrakt datatyp Public och private. Klassmedlemmar Datamedlemmar Exempel Funktionsmedlemmar

Innehåll. Typomvandlingar (casting) Implicita Typomvandlingar. Typomvandlingar (casting) Implicita Typomvandlingar

TDDI14 Objektorienterad programmering

DD2387 Programsystemkonstruktion med C++ Tentamen 1 Torsdag 7 januari 2016, 14:00-18:00

Programmering i C++ EDA623 Arv. EDA623 (Föreläsning 6) HT / 42

C++ Objektorientering - Klasser. Eric Elfving

TDDC76 PoD OH Föreläsning C Härledda klasser

Kapitel 3. Synlighet. Kapitel 3 - Klassanvändning, operatorer och pekare. Synlighet

TDIU01 - Programmering i C++, grundkurs

Nedan skapar vi klassen Person innehållande datamedlemmar för förnamn, efternamn, ålder, längd och vikt:

Synlighet. Namespace Scope-operatorn Klasser Vänner

Laboration 3. Kalkylator för aritmetiska uttryck. Kalkylatorn ska kunna läsa antingen enbokstavskommandon eller infixuttryck. Exempel på infixuttryck:

TDIU01 - Programmering i C++, grundkurs

Kalkylator för aritmetiska uttryck

Innehåll. 1 Typdeklarationer och typomvandling 2 Resurshantering. 3 Objektorientering, kort repetition. 4 Klasser

Övriga byggstenar. Övriga byggstenar. Några tips under programutveckling. Beroenden Pekare till funktioner Typkonvertering

Innehåll. Resurshantering. Resource handles. Minnesallokering. Minnesallokering Exempel: allokering på stacken. 6. Resurshantering

Del3 Klassanvändning, operatorer och pekare Ämnesområden denna föreläsning:

Introduktion till arv

allokeras på stacken dynamiskt new delete

Innehåll. Typomvandlingar (casting) Implicita Typomvandlingar. Typomvandlingar (type casts) Explicita, namngivna typomvandlingar (C++-11)

C++ Objektorientering - Klasser. Eric Elfving Institutionen för datavetenskap

Minneshantering. Minneshantering. Minneshantering. Undvik pekare

1 Klasser och objektorientering Vad är objektorientering?

TDDC76 - Programmering och Datastrukturer

Kalkylator för aritmetiska uttryck

Tommy Färnqvist, IDA, Linköpings universitet

3. Minneshantering - (copy)konstruktorn, destruktorn och tilldelningsoperatorn

Tentamen *:85/2I4123 C

Kommentarer till Person

Föreläsning 5 (6) Metoder. Metoder Deklarera. Metoder. Parametrar Returvärden Överlagring Konstruktorer Statiska metoder tostring() metoden javadoc

Tillämpad programmering

Java, klasser, objekt (Skansholm: Kapitel 2)

TDIU20 - Objektorienterad programmering i c++ - föreläsning 3

Tentamen EDAF30 Programmering i C++

Klasser. Det är egentligen nu som kursen i programmeringsteknik börjar..., s k objektorienterad programmering.

TDDC76 - Programmering och Datastrukturer

Föreläsning 2 Objektorienterad programmering DD1332. Typomvandling

TDDC76 - Programmering och Datastrukturer

Innehåll. Parametriserade typer. Klassmallar. Klassmallen Vektor Konstructor med std::initializer_list. Klassmallen Vektor Medlemsfunktioner

Lite om felhantering och Exceptions Mer om variabler och parametrar Fält (eng array) och klassen ArrayList.

Kommentarer till boken

2D1387 Programsystemkonstruktion med C++ Laboration 1: Grundläggande C++ 31 augusti 2005

Föreläsning 5-7 Operatoröverlagring

TDP004. Minne och pekare. Eric Elfving Institutionen för datavetenskap

Objektorienterad programmering Föreläsning 5

Det finns många flaggor till g++,

Objektorientering - Arv och polymorfi. Eric Elfving Institutionen för datavetenskap

Innehåll. 1 Funktionsmalllar. 2 Klassmallar. struct Name { string s; //... }; const Name & minimum ( const Name & a, const Name & b) { if(a.s < b.

Vad är en klass? Övning 2. Konstruktorer. ffl copy-konstruktorn tar en instans av klassen som. ffl default-konstruktorn tar inga argument och kan

Tentamen EDAF30 Programmering i C++

TUTORIAL: KLASSER & OBJEKT

TDDC76 - Programmering och Datastrukturer

TDDC76 - Programmering och Datastrukturer

Idag. Javas datatyper, arrayer, referenssemantik. Arv, polymorfi, typregler, typkonvertering. Tänker inte säga nåt om det som är likadant som i C.

Arrayer. results

Kopiering av objekt i Java

Innehåll. Introduktion till objektorientering. OOP (objektorienterad programmering) Objekt, instanser, klasser

TDDC76 Programmering och datastrukturer

Del2 Klasser, medlemmar och arv Ämnesområden denna föreläsning:

Innehåll. Exceptionella händelser (exceptions, undantag ) Felhantering Tre nivåer av felhantering: Nivå 2: exceptions (eller returvärde)

Tentamen EDAF30 Programmering i C++

Inledande programmering med C# (1DV402) Tärningarna ska kastas

Inkapsling tumregler. Åtkomstmodifikatorer, instantiering, referenser, identitet och ekvivalens, samt klassvariabler. public och private

TENTAMEN CD5250. Objektorienterad programutveckling med C++, 5p. Datum: , Tid: 14:00-19:00

Att deklarera och att använda variabler. Föreläsning 10. Synlighetsregler (2) Synlighetsregler (1)

Innehåll. Pekare. Datatyper Pekare, Arrayer och Referenser. Pekare Syntax. Pekare Syntax, operatorer. 4. Standard-containers. Pekare och arrayer

DAT043 - Föreläsning 7

Uppgiften är att beskriva en kvadrat i ett Java program. En första version av programmet skulle kunna se ut så här:

Ett problem. Kontrollstrukturer och arrayer. Arrayer. Lösningen. Arrayer och hakparanteser. Exempel int[] results; results = new int[10]; // 0..

Exempel. Arrayer. Lösningen. Ett problem. Arrayer och hakparanteser. Arrayer

C++ - En introduktion

Transkript:

TDDI14 Objektorienterad programmering Fö: Konstruktion av en enskild, icke-trivial klass 4 Klasser Konstruktion av en enskild, icke-trivial klass en egen strängklass String välkänd datatyp containerliknande klass ett flertal problem ska lösas intern representation hur ska strängvärdet lagras? representation för tom sträng? initiering (konstruktorer) destruering (destruktor) kopiering kopieringskonstruktor, kopieringstilldelningsoperator flyttsemantik (move-semantik) flyttkonstruktor, flyttilldelningsoperator iteratorer operationer för övrigt ett antal intressanta frågor dyker upp undantagssäkerhet kodningstekniker återanvändning

TDDI14 Objektorienterad programmering Fö: Konstruktion av en enskild, icke-trivial klass 5 Klassen String funktionalitet Vad ska man kunna göra? initiering String s1; // defaultinitiering, till tom sträng, "" String s2"foo"; // med C-sträng (litteral eller variabel) String s3s2; // genom kopiering av annan String String s4 A, h, a ; // genom element från en initierarlista tilldelning s1 = s2 s1 = "foo" s1 = A, h, a // String = String // String = char* // String = initierarlista elementåtkomst s1[i] = s2[j] s1.at(i) = s2.at(j) // utan indexkontroll // med indexkontroll storlek s1.length() s1.empty() s1.clear() // aktuell längd // tom sträng? // radera (gör till tomma strängen)

TDDI14 Objektorienterad programmering Fö: Konstruktion av en enskild, icke-trivial klass 6 Klassen String funktionalitet, forts. strängsammansättning, även i kombination med tilldelning s1 + s2 s1 + "foo" "foo" + s s1 += s2 s1 += "foo" likhet och olikhet s1 == s2 s1 == "foo" "foo" == s1 s1!= s2 s1!= "foo" "foo"!= s1 jämförelser s1 < s2 s1 <= s2 s1 > s2 s1 >= s2 s1 < "foo" s1 <= "bar" s1 > "foo" s1 >= "bar" "foo" < s "bar" <= s "foo" > s "bar" >= s

TDDI14 Objektorienterad programmering Fö: Konstruktion av en enskild, icke-trivial klass 7 Klassen String funktionalitet, forts. byta innehåll med annat String-objekt s1.swap(s2) swap(s1, s2) // som medlemsfunktion // som normal funktion iteratorer for (auto it = s.cbegin(); it!= s.cend(); ++it) cout << *it; for (auto& c : s) c = tolower(c); // String::const_iterator // char& in- och utmatning som för C-strängar cout << s1; cin >> s1; getline(cin, s1, \n ); // \n ska vara default om inget bryttecken anges

TDDI14 Objektorienterad programmering Fö: Konstruktion av en enskild, icke-trivial klass 8 Klassen String (utvalda delar) class String public: using size_type = std::size_t; String() = default; String(const String&); String(String&&) noexcept; String(const char*); ~String(); // nästlad typ // defaultkonstruktor // kopieringskonstruktor // flyttkonstruktor // typomvandlande konstruktor // destruktor String& operator=(const String&) &; // kopieringstilldelningsoperator, ref-qualifier String& operator=(string&&) & noexcept; // flyttilldelningsoperator String& operator=(const char*) &; // typomvandlande tilldelningsoperator size_type length() const; // const-funktion accessor bool empty() const; void clear(); // icke-const-funktion modifierare const char* c_str() const; void swap(string&) noexcept; // typomvandlande funktion // bör alla containerliknande klasser ha

TDDI14 Objektorienterad programmering Fö: Konstruktion av en enskild, icke-trivial klass 9 Klassen String, forts. private: static char empty_rep_[1]; size_type size_ 0 ; char* p_ empty_rep_ ; // en statisk noll-avslutad C-sträng representerar tom sträng // aktuell storlek // pekare till teckenfält där strängvärdet lagras ; void construct_(const char*, size_type); void append_(const char*); // hjälpfunktioner som används av konstruktorer // hjälpfunktioner som används av tilldelningsoperatorer alla datamedlemmar är private (HIC++ 11.1.1) alla String-objekt som är tomma strängar ska peka på gemensamma empty_rep_ size_: p_: 0 \0 ett String-objekt som inte är en tom sträng har sin eget, dynamiskt allokerade minnesutrymme för strängvärdet size_: p_: 3 C + + \0

TDDI14 Objektorienterad programmering Fö: Konstruktion av en enskild, icke-trivial klass 10 Statisk datamedlem static char empty_rep_[1]; en statisk datamedlem ingår inte i objekten klassmedlem skapas och initieras i samband med att programmet startas deklarationen i klassen är enbart en deklaration definitionen görs separat, i detta fall i filen String.cc char String::empty_rep_[1]; static anges inte i definitionen String:: före namnet anger att empty_rep_ är en medlem av String det enda tecknet i fältet initieras automatiskt till \0 en tom C-sträng

TDDI14 Objektorienterad programmering Fö: Konstruktion av en enskild, icke-trivial klass 11 Defaultkonstruktor String() = default; en defaultkonstruktor är en konstruktor som kan anropas utan argument String s; String fun() return String(); // variabeldeklaration utan initierare // temporärt objekt skapas utan argument följande konstruktor är både defaultkonstruktor och typomvandlande konstruktor String(const char* cstr = ""); String s1; String s2("c++"); // använd som defaultkonstruktor // kan ta ett argument = default innebär att konstruktorn genereras av kompilatorn medlemmar av enkel typ initieras endast om de har en initierare i sin deklaration medlemmar av klasstyp initieras av någon konstruktor beroende på hur vi kodat, defaultkonstruktorn om inte annat de icke-statiska datamedlemmarna initieras av respektive NSDMI ( non-static data member initializer ) size_type size_ 0 ; char* p_ empty_rep_ ;

TDDI14 Objektorienterad programmering Fö: Konstruktion av en enskild, icke-trivial klass 12 Medlemsinitierarlista Förekommer inte i String men är en grundläggande konstruktion (exempel kommer lägre fram i kursen). en egen defaultkonstruktor hade kunnat skrivas String() : size_ 0, p_ empty_rep_ en medlemsinitierarlista kan finnas mellan parameterlistan och funktionskroppen inleds med kolon och består av kommaseparerade medlemsinitierare skriv medlemsinitierarna i samma ordning som medlemmarna deklareras (HIC++ 12.4.4) om deklarationen av en datamedlem har en initierare utförs inte den om det finns en medlemsinitierare dubbelinitiering görs inte i sådana fall specificera inte både NSDMI och medlemsinitierare i konstruktorer (HIC++ 12.4.3) samtliga konstruktorer för String visar sig med fördel kunna implementeras med hjälp av NSDMI size_type size_ 0 ; char* p_ empty_rep_ ; inga medlemsinitierare förekommer i kodexemplet därefter sker ytterligare åtgärder med någon av hjälpfunktionerna construct_() eller swap()

TDDI14 Objektorienterad programmering Fö: Konstruktion av en enskild, icke-trivial klass 13 Typomvandlande konstruktor String(const char*); en konstruktor som kan anropas med ett argument av en annan typ är en typomvandlande konstruktor String s"c++"; litteralen C++ har typen const char[4] s har typen String typomvandling sker syntaxen ovan kallas direktinitiering kan även skrivas med vanliga parenteser String s("c++"); följande deklarationssyntax kallas kopieringsinitiering String s1 = s2; String s3 = "C++"; String s4 = String"C++"; // samma typ kopieringskonstruktorn gör initiering // implicit typomvandling temporärt objekt skapas (optimeras bort) // explicit typomvandling temporärt objekt skapas (optimeras bort) flyttkonstruktorn gör initieringen i de två senare fallen (kommer strax ) optimering kan förekomma observera denna syntax har inget med tilldelning att göra! alla explicita typomvandlingar sker med denna konstruktor, exempelvis auto s5 = static_cast<string>("c++");

TDDI14 Objektorienterad programmering Fö: Konstruktion av en enskild, icke-trivial klass 14 Destruktor String s"foo"; // objektet s skapas String* p = new String"bar"; // ett dynamiskt String-objekt skapas delete p; // minnet för det dynamiska objektet återlämnas objektet försvinner // deklarationsblocket för s lämnas objektet s försvinner destruktorn körs alltid då ett String-objekt är på väg att försvinna ska säkerställa att det dynamiska minnet återlämnas String::~String() if (!empty()) delete[] p_; minnet har skapats av new[] minnet måste återlämnas med delete[] datamedlemmarna size_ och p_ försvinner med själva String-objektet kan något oönskat hända när delete[] utförs?

TDDI14 Objektorienterad programmering Fö: Konstruktion av en enskild, icke-trivial klass 15 Kopieringskonstruktor Djup kopiering. String::String(const String& other) construct_(other.p_, other.size_); String s1"c++"; // size_ och p_ initieras av sina initierare tom sträng s1 3 C + + \0 String s2s1; ett nytt objekt skapas ingen historik som behöver tas hänsyn till s2 3 C + + \0 hjälpfunktionen construct_ tar hand om detaljerna den kompilatorgenererade konstruktorn skulle endast kopiera size_ och p_ teckenfältet skulle delas av flera objekt s1 3 C + + \0 s2 3

TDDI14 Objektorienterad programmering Fö: Konstruktion av en enskild, icke-trivial klass 16 Hjälpfunktioner för konstruktion och tilldelning Privata hjälpfunktioner används för att utföra detaljerna vid initiering och tilldelning. Exempel: void String::construct_(const char* cstr, size_type size) if (cstr!= nullptr && size > 0) p_ = strcpy(new char[size + 1], cstr); size_ = size; farliga saker görs på speciella ställen i construct_() och append_() dynamiska minnesallokering om new misslyckas kastas undantaget bad_alloc inget kan hända vid kopieringen av tecknen inget kan hända vid tilldelningen av size_ om new kastar avbryts construct_() och i sin tur konstruktorn (alternativt tilldelningsoperatorn i fråga) objektet kunde inte skapas (tilldelas) det är inget som behöver göras på grund av att undantag kastas implementering av construct_() ovan hanterar de tre tänkbara fallen cstr är en tompekare (nullptr) borde inte ske men är möjligt resultatet blir en tom sträng cstr pekar på en tom sträng (size == 0) resultatet ska vara en tom sträng cstr pekar på en icke-tom sträng resultatet ska vara en kopia av indata

TDDI14 Objektorienterad programmering Fö: Konstruktion av en enskild, icke-trivial klass 17 Undantagssäkerhet Rimligt beteende om undantag kastas. Tre nivåer: grundläggande garanti undantagssäkert om undantag kastas förblir objekt i ett tillåtet tillstånd kanske inte det ursprungliga dock inga resurser förloras till exempel dynamiskt minne stark garanti starkt undantagssäkert operation lyckas antingen helt, eller så kastas undantag men programmet bibehåller det tillstånd det hade innan operationen påbörjades inga objekt påverkas kastar-inte-garanti operationen kastar inte undantag destruktorer kastar aldrig undantag deklarera dock inte det med nothrow Undantagsneutralitet en undantagsneutral funktion vidarebefordrar kastade undantag normalt samma undantag som ursprungligen kastats lokala åtgärder kan ha behövt vidtas innan undantaget vidarebefordras

TDDI14 Objektorienterad programmering Fö: Konstruktion av en enskild, icke-trivial klass 18 Kopieringstilldelningsoperator Djup tilldelning. s1 = s2; s1: 6 S c h e m e \0 vänsteroperanden har historik gammalt innehåll behöver tas om hand s2: 3 C + + \0 kopiera nytt värde från högeroperanden viktigt att göra saker i rätt ordning inget objekt ska hamna i ett odefinierat tillstånd s1: 3 C + + \0 se först till att nytt minne erhålls gör sedan ändringar s2: 3 C + + \0 glöm inte möjligheten s1 = s1; S c h e m e \0 inte alltid så uppenbart den kompilatorgenererade kopieringstilldelningsoperator skulle endast ha tilldelat size_ och p_ s1 och s2 hade kommit att dela på samma teckenfält det gamla teckenfältet för s1 hade tappats bort (minnesläcka)

TDDI14 Objektorienterad programmering Fö: Konstruktion av en enskild, icke-trivial klass 19 Kopieringstilldelningsoperator rättfram implementering String& String::operator=(const String& rhs) & // ref-qualifier, HIC++ 12.5.7 if (this!= &rhs) char* p = empty_rep_; // ifall rhs är en tom String if (!rhs.empty()) p = strcpy(new char[rhs.size_ + 1], rhs.p_); size_ = rhs.size_; if (!empty()) delete[] p_; p_ = p; return *this; vänsteroperanden har gammalt innehåll att ta hand om kontrollerar om vänster och höger operand är samma objekt självtest // om vi överlever detta är vi säkra this är en pekare till det objekt som medlemsfunktionen har anropats för i detta fall en pekare till vänsteroperanden s = s utför djup kopiering på ett starkt undantagssäkert sätt allokerar minne innan något ändras om new kastar inget minne kommer att läcka inget av objekten kommer att vara ändrade egenskaper som en inbyggd tilldelningsoperator returnerar icke-const referens (lvalue) till vänsterargumentet, String&

TDDI14 Objektorienterad programmering Fö: Konstruktion av en enskild, icke-trivial klass 20 Kopieringstilldelningsoperator elegant implementering String& String::operator=(const String& rhs) & String rhs.swap(*this); return *this; // skapa en temporär och byt använder idiomet skapa en temporär och byt ( create a temporary and swap, HIC++ 12.5.6) ref-qualifier & medför att operator= endast kan användas om vänsteroperanden är ett lvalue-uttryck (namn på variabel) typen för vänsteroperanden är lvalue-referens till String (String&) tilldelningsoperatorer ska alltid deklareras så får samma egenskap som de inbyggda operatorerna har behöver en funktion som kan byta innehåll hos två String-objekt på ett säker sätt, helst en nothrow swap (noexcept) en temporär skapas och initieras av kopieringskonstruktorn djup kopia av rhs innehållet hos vänsteroperanden (*this) och temporären byts *this blir en kopia av rhs temporären tar över det gamla innehållet hos *this speciellt det dynamiskt allokerade teckenfältet temporären destrueras efter swap genomförts det gamla dynamiska minnet för *this återlämnas om ett undantag kastas, kommer det att inträffa då temporären initieras av kopieringskonstruktorn starkt undantagssäkert inget minne läcker inga objekt hamnar i ett odefinierat tillstånd undantagsneutralt ett undantag som kastas förs vidare som det är självtest skulle kunna göras if (this!= &rhs) String rhs.swap(*this);

TDDI14 Objektorienterad programmering Fö: Konstruktion av en enskild, icke-trivial klass 21 De användbara swap-funktionerna Grundregel: alla datatyper som kan ha swap() bör (ska) ha det. swap() som medlem anropar standardbibliotekets swap void String::swap(String& rhs) noexcept std::swap(p_, rhs.p_); std::swap(size_, rhs.size_); swap() som normal funktion anropar medlemmen void swap(string& lhs, String& rhs) noexcept lhs.swap(rhs); två viktiga aspekter återanvändning samt undvika att göra till friend specialisering av swap() för String väljs i stället för standardbibliotekets swap() då argumenten är String-objekt std::swap() kastar inget undantag om inte operator= för typen ifråga kastar undantag operator= för grundläggande typer och pekare kastar inte undantag String::swap() kastar inte undantag void String::swap(String& rhs) noexcept; void swap(string& lhs, String& rhs) noexcept;

TDDI14 Objektorienterad programmering Fö: Konstruktion av en enskild, icke-trivial klass 22 Typomvandlande tilldelningsoperator String& String::operator=(const char* rhs) & String rhs.swap(*this); return *this; s1 = "foobar"; typomvandlande tilldelningsoperator från C-sträng (char[], char*) till String implementeras med idiomet skapa en temporär och byt konstruktorn String(const char*) används för att skapa det temporära objektet

TDDI14 Objektorienterad programmering Fö: Konstruktion av en enskild, icke-trivial klass 23 Typomvandling Vi har sett två exempel redan String(const char* rhs); String& operator=(const char* rhs) &; // typomvandlande konstruktor // typomvandlande tilldelningsoperator en sådan konstruktor kan användas implicit String s = "C++"; // implicit typomvandling, temporär skapas vill man förbjuda det kan man deklarera konstruktorn explicit (inte för String) tumregeln är att deklarera explicit explicit String(const char* rhs); det finns en vanlig medlemsfunktion som gör typomvandling från String till const char* (C-sträng) const char* c_str() const return p_; man kan deklarera en typomvandlande operator (medlem) som gör typomvandling från String till const char* operator const char*() const return p_; const char* ps; // implicit typomvandling deklarera inte implicit typomvandling deklarera explicit (HIC++ 12.1.1) explicit operator const char*() const return p_; const char* p = static_cast<const char*>(s); // explicit typomvandling

TDDI14 Objektorienterad programmering Fö: Konstruktion av en enskild, icke-trivial klass 24 Flyttsemantik En av de stora nyheterna i C++11 alternativ till traditionell kopieringssemantik. temporära objekt skapas i många olika situationer ofta implicit om ett temporärt objekt används för att initiera eller tilldela ett annat objekt är det onödigt att göra en kopia kopiering kan vara kostsamt tid och utrymme flytta i stället innehållet från temporären till destinationsobjektet hur finner man sådana objekt de syns ju vanligvis inte? kompilatorn vet! rvalue-referenser fångar dem automatiskt! vi behöver bara vara medvetna om möjligheten och ha det i åtanke när vi konstruerar klasser String(const String&); // denna kan fånga alla slags objekt men String(String&&) noexcept; // denna är en bättre match för temporära objekt String& operator=(const String&) &; String& operator=(string&&) & noexcept; implementering av flyttsemantik ett objekt vars resurser har flyttats måste vara destruerbart ibland vill vi applicera flyttsemantik även på vanliga objekt (variabler) ett objekt vars innehåll har flyttats måste vara tilldelningsbart och kopierbart bör motsvara ett defaultinitierat objekt tom sträng i fallet String flyttkonstruktor och flyttilldelningsoperator ska alltid deklareras noexcept inga undantag ska kastas

TDDI14 Objektorienterad programmering Fö: Konstruktion av en enskild, icke-trivial klass 25 Hjälpfunktionen std::move() Det traditionella sättet att byta värde på två String-variabler: void swap(string& x, String& y) String tmp x ; x = y; y = tmp; // x kopieras till tmp av kopieringskonstruktorn // y kopieras till x av kopieringstilldelningsoperatorn // tmp kopieras till y av kopieringstilldelningsoperatorn Både x och y ska erhålla nya värden deras gamla värden behöver inte behållas då de kopieras flytta i stället #include <utility> void swap(string& x, String& y) String tmp std::move(x) ; x = std::move(y); y = std::move(tmp); // x flyttas till tmp av flyttkonstruktorn // y flyttas till x av flyttilldelningsoperatorn // tmp flyttas till y av flyttilldelningsoperatorn hjälpfunktionen move() gör inget mer än att typomvandlar sitt argument till en rvalue-referens String&& i detta fall detta leder till användning av flyttoperationerna Anm. move() gör i princip typomvandlingen static_cast<string&&>(s)

TDDI14 Objektorienterad programmering Fö: Konstruktion av en enskild, icke-trivial klass 26 Flyttkonstruktor String::String(String&& other) noexcept swap(other); // size_ och p_ initieras av sina initierare tom sträng initialt // byt innehåll med other other blir tom sträng String s1 = String"C++"; String s2 std::move(s1) ; // hjälpfunktionen move() omvandlar s1 (lvalue) till String&& (rvalue-referens) flyttkonstruktorn väljs i exemplen ovan om den finns, i stället för kopieringskonstruktorn initieringen av s2 innebär följande vid inträdet i konstruktorkroppen efter flytt s1: 3 C + + \0 0 \0 s2: 0 \0 3 C + + \0 bör deklareras noexcept (HIC++ 12.5.4)

TDDI14 Objektorienterad programmering Fö: Konstruktion av en enskild, icke-trivial klass 27 Flyttilldelningsoperator Vår implementering använder clear() och swap(): String& String::operator=(String&& rhs) & noexcept // HIC++ 12.5.7, 12.5.4 clear(); // vänsteroperanden sätts till tom sträng swap(rhs); // flytt utförs genom att byta innehåll på vänster- och högeroperanden return *this; s1 = s2; före tilldelning x nollställd efter flytt s1: 1 0 3 C \0 \0 C + + \0 s2: 3 3 0 C + + \0 C + + \0 \0 Ett alternativ är att bara låta objekten byta innehåll, dvs utan att först göra clear() på vänsterargumentet.

TDDI14 Objektorienterad programmering Fö: Konstruktion av en enskild, icke-trivial klass 28 Konstruktor som tar en initierarlista String::String(std::initializer_list<char> il) construct_(il); void String::construct_(initializer_list<char> il) if (il.size() > 0) size_ = il.size(); p_ = new char[size_ + 1]; std::copy(il.begin(), il.end(), p_); p_[size_] = \0 ; String s G, a, z, o, n, k ; std::initializer_list är en klassmall som instansieras för elementtypen i fråga (char) il initieras från initierarlistan som anges i deklarationen av String-objektet std::initializer_list har iteratorer och tre operationer size(), begin() och end()

TDDI14 Objektorienterad programmering Fö: Konstruktion av en enskild, icke-trivial klass 29 Tilldelningsoperator som tar en initierarlista String& String::operator=(initializer_list<char> rhs) & String rhs.swap(*this); return *this; s = G, a, z, o, n, k ; implementeras med idiomet skapa en temporär och byt konstruktorn som tar en initierarlista används för att skapa ett temporärt objekt

TDDI14 Objektorienterad programmering Fö: Konstruktion av en enskild, icke-trivial klass 30 Delegerande konstruktorer Inga konstruktorer hos String lämpar sig för att exemplifiera detta detta exempel är taget från Fö 5 (polymorf lista). class List public: List() : head_ new Null_Node; // en tom lista innehåller en Null_Node List(List&& other) noexcept : List() swap(other); void swap(list& other) noexcept std::swap(head_, other.head_); private: List_Node* head_; ; flyttkonstruktorn delegerar till defaultkonstruktorn att initiera det nya List-objektet till tom lista sedan byts innehåll med other

TDDI14 Objektorienterad programmering Fö: Konstruktion av en enskild, icke-trivial klass 31 Kompilatorgenererade versioner av speciella medlemsfunktioner defaultkonstruktorn skulle motsvara String() datamedlemmar av grundläggande typ, t.ex. int, eller pekare initieras inte datamedlemmar av klasstyp initieras av sin defaultkonstruktor om det finns initierare i deklarationerna för datamedlemmar utförs de kopieringskonstruktorn skulle kopieringsinitiera datamedlemmarna i den ordning de deklareras String(const String& rhs) : p_ rhs.p_, size_ rhs.size_ flyttkonstruktorn skulle flyttinitiera datamedlemmarna i den ordning de deklareras String(String&& rhs) noexcept : p_ std::move(rhs.p_), size_ std::move(rhs.size_) ingen egentlig skillnad jämfört med kopieringskonstruktorn för dessa typer destruktorn skulle motsvara ~String() datamedlemmar av klasstyp destrueras av sina destruktorer (i omvänd deklarationsordning) delete[] p_ görs inte dynamiska minnet skulle läcka

TDDI14 Objektorienterad programmering Fö: Konstruktion av en enskild, icke-trivial klass 32 Kompilatorgenererade versioner av speciella medlemsfunktioner, forts. kopieringstilldelningsoperatorn skulle kopieringstilldela datamedlemmarna i den ordning de deklareras String& operator=(const String& rhs) & p_ = rhs.p_; size_ = rhs.size_; kopieringstilldelningsoperatorn för respektive typ utförs flyttilldelningsoperatorn skulle flyttilldela datamedlemmarna i den ordning de deklareras String& operator=(const String& rhs) & noexcept p_ = std::move(rhs.p_); size_ = std::move(rhs.size_); flyttilldelningsoperatorn för respektive typ utförs p_ och size_ är typer för vilka inte flyttilldelning finns (är meningsfull) Vi måste alltså deklarera samtliga dessa själva för att erhålla korrekt initiering, destruering och kopiering.

TDDI14 Objektorienterad programmering Fö: Konstruktion av en enskild, icke-trivial klass 33 Iteratorer för String String är en containertyp med element av typen char. s: 3 C + + \0 class String public: using iterator using const_iterator = char*; = const char*; it: using reverse_iterator using const_reverse_iterator = std::reverse_iterator<iterator>; = std::reverse_iterator<const_iterator>; förutsättningarna för Random Access-iteratorer uppfylls iteratorer kan definieras som vanliga teckenpekare (char*, const char*) de inbyggda operatorerna för pekartypena ger och all funktionalitet som behövs för att operera på iteratorerna bakåtiteratorer definieras med hjälp av std::reverse_iterator

TDDI14 Objektorienterad programmering Fö: Konstruktion av en enskild, icke-trivial klass 34 Iteratorer för String, forts. String ska ha full uppsättning iterator-funktioner iterator begin() return iterator(p_); const_iterator begin() const return const_iterator(p_); iterator end() return iterator(p_ + size_); const_iterator end() const return const_iterator(p_ + size_); reverse_iterator rbegin() return reverse_iterator(end()); const_reverse_iterator rbegin() const return reverse_iterator(end()); reverse_iterator rend() return iterator(begin()); const_reverse_iterator rend() const return const_iterator(begin()); const_iterator cbegin() const return const_iterator(p_); const_iterator cend() const return const_iterator(p_ + size_); const_reverse_iterator crbegin() const return const_reverse_iterator(end()); const_reverse_iterator crend() const return const_reverse_iterator(begin()); eftersom iteratorerna är vanliga pekare fungerar de inbygga operatorerna för stegning, jämförelse, avreferering, etc. for (String::const_iterator it = s.cbegin(); it!= s.cend(); ++it) cout << *it; men hellre auto för att deklarera it range access -funktionerna kan användas

TDDI14 Objektorienterad programmering Fö: Konstruktion av en enskild, icke-trivial klass 35 Operatoröverlagring endast operatorsymboler definierade i C++ kan överlagras + - * / % ^ & ~! << >> = += -= *= /= %= ^= &= = <<= >>= < > <= >= ==!= && ++ -- -> ->*, [ ] ( ) new new[] delete delete[] fyra operatorer är inte tillåtna att överlagra..* ::?: de tre första är fundamentala för åtkomst och deklaration av klassmedlemmar?: har en strikt ordning för beräkning av argumenten som inte kan specificeras för en egen överlagring fyra operatorer måste vara en icke-statisk medlemsfunktion = () [] -> säkerställer att vänsteroperanden är ett objekt av typen ifråga övriga kan antingen vara en icke-statisk medlemsfunktion med ingen parameter (unär operator) eller en parameter (binär operator) this finns implicit icke-medlemsfunktion med en parameter (unär operator) eller två parametrar (binär operator)

TDDI14 Objektorienterad programmering Fö: Konstruktion av en enskild, icke-trivial klass 36 Operatoröverlagring, forts. anropssyntaxen kan väljas som vanligt för operatorer (infix-, prefix- eller postfix-notation) a + b om medlem vanligt medlemsfunktionsanrop a.operator+(b) om icke-medlem vanligt funktionsanrop operator+(a, b) prioritet och associativitet gäller enligt de inbyggda operatorerna beräkningsordningen för argument kan inte styras medför problem vid överlagring av exempelvis && och överlagra inte sådana operatorer (HIC++ 13.2.1) välj parametertyper och resultattyp med omsorg se till att returtypen för en binär operator matchar dess inbyggda motsvarighet (HIC++ 13.2.2) deklarera binära aritmetiska operatorer och bitvisa operatorer som icke-medlemmar (HIC++ 13.2.3) tänk på att icke-medlemsfunktioner ska deklareras i samma namnrymd som typen de tillhör (String i detta fall) ADL (Argument Dependent Lookup) kan annars ställa till det funktionen hittas inte eller, i värsta fall, väljs fel funktion

TDDI14 Objektorienterad programmering Fö: Konstruktion av en enskild, icke-trivial klass 37 Indexeringsoperator operator[] gör ingen kontroll av indexvärdet ska alltid implementeras med en icke-const- och en const-version (HIC++ 13.2.4) char& String::operator[](int pos) return p_[pos]; char String::operator[](int pos) const return p_[pos]; // för icke-const String // för const String motsvarande medlemsfunktion at() kontrollerar om given position (index) är tillåten om inte kastas undantaget out_of_range char& String::at(int pos) if (pos < 0 pos >= size_) throw out_of_range("string::at()"); return p_[pos]; char String::at(int pos) const if (pos < 0 pos >= size_) throw out_of_range("string::at()"); return p_[pos]; // för icke-const String // för const String

TDDI14 Objektorienterad programmering Fö: Konstruktion av en enskild, icke-trivial klass 38 Strängsammansättning s1 += s2; s3 = s1 + s2: sammansättning av två String-objekt med += String& String::operator+=(const String& rhs) if (!rhs.empty()) append_(rhs.p_); return *this; sammansättning av två String-objekt med + String operator+(const String& lhs, const String& lhs) return String lhs.operator+=(rhs); operatorer som + och += hör ihop finns den ena förväntas den andra också finnas operator+ implementeras med hjälp av operator+= (HIC++ 13.2.5) säkerställer konsekvent semantik för += och + exempel på när man använder vanlig medlemsfunktionsanropssyntax för operatorfunktion

TDDI14 Objektorienterad programmering Fö: Konstruktion av en enskild, icke-trivial klass 39 Operatoröverlagring riktlinjer (se även HIC++ 13.2) om operatorn är en av följande kan den inte överlagras..* :: :? om operatorn är en av följande måste den vara medlem = -> [] () om operatorn 1. kan ha en annan typ som vänsterargument, eller den 2. kan ha typomvandling för sitt vänsterargument, eller den 3. kan implementeras enbart med publika medlemsfunktioner, gör den till icke-medlem och, om nödvändigt i fall 1 och 2, även friend om implementeringen kräver åtkomst till icke-publika medlemmar om operatorn behöver bete sig virtuellt, lägg till en virtuell medlemsfunktion och låt operatorn anropa den medlemsfunktionen (av intresse i samband med polymorfa klasser) i annat fall, låt operatorn vara icke-medlem det finns dock flera operatorer som det vanligtvis är naturligt att (vänster)operanden ska vara ett objekt av typen ifråga: *= /= %= += -= &= = ^= <<= >>= ++ -- överlagra inte operatorer med speciell semantik, dvs &&, och, (kommaoperatorn) (HIC++ 13.2.1) samtliga vänsterassociativa, högeroperanden till && och ska enbart beräknas om vänsteroperanden beräknats true/false överlagring av adressoperatorn & kan leda till odefinierat beteende om den används på ställe där den egna inte är synlig

TDDI14 Objektorienterad programmering Fö: Konstruktion av en enskild, icke-trivial klass 40 Sammanfattning Vi har studerat en icke-trivial klass och i samband med den tagit upp ett antal viktiga saker. initiering konstruktorer av olika slag defaultkonstruktor kopieringskonstruktor och flyttkonstruktor andra konstruktorer, bland annat typomvandlande destruering String-objekt i sig återtas automatiskt då deklarationsblocket lämnas eller delete utförs på ett dynamiskt String-objekt det tillhörande dynamiska minnet måste uttryckligen återlämnas delete[] p_ tilldelning kopieringstilldelning och flyttilldelning andra tilldelningar, bland annat typomvandlande olika operationer överlagrade operatorer och vanliga funktioner medlem eller icke-medlem? swap-funktioner är mycket användbara för containerliknande klasser mycket viktigt att deklarera medlemsfunktioner som inte ändrar på datamedlemmar const (HIC++ 9.1.1) alltid viktigt att använda const för saker som inte ska ändras objekt, medlemsfunktioner, parametrar, iteratorer visade sig enkelt i detta fall (char*)

TDDI14 Objektorienterad programmering Fö: Konstruktion av en enskild, icke-trivial klass 41 Sammanfattning, forts. vi har lyckats undvika att vän-deklarera (friend) icke-medlemmar (HIC++ 11.2) publika medlemsfunktioner som ändå ska finnas används typomvandling tillåts under kontrollerade former behovet av implicit typomvandling har minimerats tumregeln säger annars att det bör elimineras vissa binära operatorer har överlagrats i versioner som kan ta String och char* blandat förekomsten av temporära objekt minimeras implicit typomvandling från char* till String tillåts får anses problemfri och användbart endast explicit typomvandling till char* tillåts (HIC++ 12.1.1) eftersom det är en pekartyp skulle annars mycket kunna hända in- och utmatning funktionalitet och implementation i analogi med inbyggda datatyper undantagssäker programmering genomtänkt kodning i situationer då undantag kan kastas inga objekt blir defekta inget minne läcker användbart idiom skapa en temporär och byt (HIC++ 12.5.6) uppsättningen operationer behöver utvidgas för att få en användbar strängtyp