TIE-20200 Ohjelmistojen suunnittelu. Kopiointia ja sijoittelua



Samankaltaiset tiedostot
TIE Ohjelmistojen suunnittelu. Kopiointia ja sijoittelua

TIE Ohjelmistojen suunnittelu

Osoitin ja viittaus C++:ssa

815338A Ohjelmointikielten periaatteet Harjoitus 5 Vastaukset

4. Luokan testaus ja käyttö olion kautta 4.1

C++11 lambdat: [](){} Matti Rintala

TIE Ohjelmistojen suunnittelu. Luento 8..9: moniperintä

Operaattoreiden ylikuormitus. Operaattoreiden kuormitus. Operaattoreiden kuormitus. Operaattoreista. Kuormituksesta

Virtuaalifunktiot ja polymorfismi

Rajapinta (interface)

Rajapinnat ja olioiden välittäminen

T Olio-ohjelmointi Osa 5: Periytyminen ja polymorfismi Jukka Jauhiainen OAMK Tekniikan yksikkö 2010

JAVA-PERUSTEET. JAVA-OHJELMOINTI 3op A JAVAN PERUSTEET LYHYT KERTAUS JAVAN OMINAISUUKSISTA JAVAN OMINAISUUKSIA. Java vs. C++?

Olio-ohjelmointi Syntaksikokoelma

812347A Olio-ohjelmointi, 2015 syksy 2. vsk. X Poikkeusten käsittelystä

Olion elinikä. Olion luominen. Olion tuhoutuminen. Olion tuhoutuminen. Kissa rontti = null; rontti = new Kissa();

C++11 Syntaksi. Jari-Pekka Voutilainen Jari-Pekka Voutilainen: C++11 Syntaksi

12 Mallit (Templates)

13 Operaattoreiden ylimäärittelyjä

815338A Ohjelmointikielten periaatteet Harjoitus 3 vastaukset

2. Olio-ohjelmoinista lyhyesti 2.1

15. Ohjelmoinnin tekniikkaa 15.1

Sisällys. 7. Oliot ja viitteet. Olion luominen. Olio Java-kielessä

Tietueet. Tietueiden määrittely

7. Oliot ja viitteet 7.1

Luokassa määriteltävät jäsenet ovat pääasiassa tietojäseniä tai aliohjelmajäseniä. Luokan määrittelyyn liittyvät varatut sanat:

Sisällys. Yleistä attribuuteista. Näkyvyys luokan sisällä. Tiedonkätkentä. Aksessorit. 4.2

Ohjelman virheet ja poikkeusten käsittely

TIE Ohjelmistojen suunnittelu

11/20: Konepelti auki

T Olio-ohjelmointi Osa 3: Luokka, muodostin ja hajotin, this-osoitin Jukka Jauhiainen OAMK Tekniikan yksikkö 2010

Periytyminen. Luokat ja olio-ohjelmointi

1. Olio-ohjelmointi 1.1

15. Ohjelmoinnin tekniikkaa 15.1

Demo 6 vastauksia. 1. tehtävä. #ifndef #define D6T1 H D6T1 H. #include <iostream> using std::ostream; using std::cout; using std::endl;

C++ rautaisannos. Kolme tapaa sanoa, että tulostukseen käytetään standardikirjaston iostreamosassa määriteltyä, nimiavaruuden std oliota cout:

Metodien tekeminen Javalla

Kooste. Esim. Ympyrän keskipiste voidaan ajatella ympyrän osaksi.

Olio-ohjelmointi Javalla

ITKP102 Ohjelmointi 1 (6 op)

Luento 4 Aliohjelmien toteutus

16. Javan omat luokat 16.1

Jakso 4 Aliohjelmien toteutus

Operaattoreiden uudelleenmäärittely

TIE Ohjelmistojen suunnittelu

Sisällys. JAVA-OHJELMOINTI Osa 7: Abstrakti luokka ja rajapinta. Abstraktin luokan idea. Abstrakti luokka ja metodi. Esimerkki

Sisällys. Yleistä attribuuteista. Näkyvyys luokan sisällä ja ulkopuolelta. Attribuuttien arvojen käsittely aksessoreilla. 4.2

Metodit. Metodien määrittely. Metodin parametrit ja paluuarvo. Metodien suorittaminen eli kutsuminen. Metodien kuormittaminen

812347A Olio-ohjelmointi, 2015 syksy 2. vsk. IX Suunnittelumallit Proxy, Factory Method, Prototype ja Singleton

Harjoitus Olkoon olemassa luokat Lintu ja Pelikaani seuraavasti:

Sisällys. 9. Periytyminen Javassa. Periytymismekanismi Java-kielessä. Periytymismekanismi Java-kielessä

Aalto Yliopisto T Informaatioverkostot: Studio 1. Oliot ja luokat Javaohjelmoinnissa

14. oppitunti. Operaattorin ylikuormitus. Osa. Operaattorin ylikuormittaminen

Kompositio. Mikä komposition on? Kompositio vs. yhteyssuhde Kompositio Javalla Konstruktorit set-ja get-metodit tostring-metodi Pääohjelma

4. Olio-ohjelmoinista lyhyesti 4.1

Javan perusteita. Janne Käki

Java kahdessa tunnissa. Jyry Suvilehto

12. Monimuotoisuus 12.1

Geneeriset luokat. C++ - perusteet Java-osaajille luento 6/7: Template, tyyppi-informaatio, nimiavaruudet. Geneerisen luokan käyttö.

812347A Olio-ohjelmointi, 2015 syksy 2. vsk. VII Suunnittelumallit Adapter ja Composite

Muutamia peruskäsitteitä

Sisällys. 1. Omat operaatiot. Yleistä operaatioista. Yleistä operaatioista

9. Periytyminen Javassa 9.1

OHJ-1450 Olio-ohjelmoinnin jatkokurssi lisämateriaalia

TIE Samuel Lahtinen. Lyhyt UML-opas. UML -pikaesittely

1. Omat operaatiot 1.1

9. Periytyminen Javassa 9.1

Luokat. Luokat ja olio-ohjelmointi

Tehtävä 1. TL5302 Olio-ohjelmointi Koe Malliratkaisuja. Tässä sekä a)- että b)-kohdan toimiva ratkaisu:

A) on käytännöllinen ohjelmointitekniikka. = laajennetaan aikaisemmin tehtyjä luokkia (uudelleenkäytettävyys)

Oliosuunnitteluesimerkki: Yrityksen palkanlaskentajärjestelmä

812341A Olio-ohjelmointi, IX Olioiden välisistä yhteyksistä

Mikä yhteyssuhde on?

11. oppitunti III. Viittaukset. Osa. Mikä on viittaus?

C-kielessä taulukko on joukko peräkkäisiä muistipaikkoja, jotka kaikki pystyvät tallettamaan samaa tyyppiä olevaa tietoa.

Listarakenne (ArrayList-luokka)

Olio-ohjelmoinnissa luokat voidaan järjestää siten, että ne pystyvät jakamaan yhteisiä tietoja ja aliohjelmia.

Ohjelmointikieli TIE Principles of Programming Languages Syksy 2017 Ryhmä 19

TIE Principles of Programming Languages CEYLON

Opintojakso TT00AA11 Ohjelmoinnin jatko (Java): 3 op Rajapinnat ja sisäluokat

Sisällys. 9. Periytyminen Javassa. Periytymismekanismi Java-kielessä. Periytymismekanismi Java-kielessä

Ohjelmoinnin peruskurssien laaja oppimäärä

812341A Olio-ohjelmointi Peruskäsitteet jatkoa

Kääntäjän virheilmoituksia

Opintojakso TT00AA11 Ohjelmoinnin jatko (Java): 3 op Taulukot & Periytyminen

Ohjelmointi 2. Jussi Pohjolainen. TAMK» Tieto- ja viestintäteknologia , Jussi Pohjolainen TAMPEREEN AMMATTIKORKEAKOULU

1 Tehtävän kuvaus ja analysointi

Mallit standardi mallikirjasto parametroitu tyyppi

Sisältö. 2. Taulukot. Yleistä. Yleistä

Sisällys. 6. Metodit. Oliot viestivät metodeja kutsuen. Oliot viestivät metodeja kutsuen

Ohjelmoinnin jatkokurssi, kurssikoe

Sisällys. JAVA-OHJELMOINTI Osa 6: Periytyminen ja näkyvyys. Luokkahierarkia. Periytyminen (inheritance)

Aliohjelmatyypit (2) Jakso 4 Aliohjelmien toteutus

ITKP102 Ohjelmointi 1 (6 op)

Yleistä. Nyt käsitellään vain taulukko (array), joka on saman tyyppisten muuttujien eli alkioiden (element) kokoelma.

Olio-ohjelmointi Suunnittelumallit Proxy, Factory Method, Prototype ja Singleton. 1. Proxy (Edustaja)

TIE448 Kääntäjätekniikka, syksy Antti-Juhani Kaijanaho. 27. lokakuuta 2009

Ohjelmointiharjoituksia Arduino-ympäristössä

AS C-ohjelmoinnin peruskurssi 2013: C-kieli käytännössä ja erot Pythoniin

Taulukot. Jukka Harju, Jukka Juslin

Transkriptio:

TIE-20200 Ohjelmistojen suunnittelu Kopiointia ja sijoittelua 1

Elinkaari Olion elinkaari: olion tapahtumat luomisesta tuhoutumiseen Ei-olio-ohjelmoinnissa ei yleensä tärkeä(?) Olio-ohjelmoinnissa merkitys korostuu Miksi? Eroja eri oliokielissä 2

Olion syntymä ja kuolema Olion syntyessä: Jäsenmuuttujien luominen Muistinvaraus Muut tarvittavat oliot Rekisteröitymiset Resurssit... Osa automatisoitu, osa ohjelmoijan vastuulla Olion tuhoutuessa yleensä vastakkaiset toimenpiteet Automaattisuus jälleen kieliriippuvainen 3

Elinkaaren määräytyminen Riippuu kielestä: Vain muistinvaraus Muistinvaraus & roskienkeruu Alustustoimenpiteet automaattisia Siivoustoimenpiteet automaattisia, ajankohta ei määrätty Siivous automaattista, tuhoutumishetki määrätty 4

Rakentajien ongelmia C++ Jos käyttäjä ei määrittele yhtään rakentajaa, luodaan automaattisesti oletusrakentaja Generoitu rakentaja ei alusta muuttujia mihinkään arvoon Älä siis luota generoituun rakentajaan, vaan määrittele aina rakentaja itse. Rakentaja voi toimia tyyppimuunnosoperaattorina // Paivays( int p = 1, int k = 1, int vuosi = 2013 ); // funktio laskee montako päivää on tuomiopäivään int paiviatuomiopaivaan( Paivays pvm ); int jaljella = paiviatuomiopaivaan( 4 ); // Mitäs tuossa yllä tapahtuikaan, virhe? 5

Tyyppimuunnokset Älä käytä c-tyylin tyyppimuunnoksia. Jos haluat tehdä tyyppimuunnoksen, käytä C++:n tyyppimuunnoksia static_cast< uusityyppi >( muunnettava ); Esimerkki: char mychar = 'c'; int charval = static_cast< int >( mychar ); // charval = 99 --charval; mychar = static_cast< char >( charval ); // mychar = b Jos implisiittiset tyyppimuunnokset haluaa kieltää, pitää käyttää explicit avainsanaa rakentajan yhteydessä: class Paivays public: explicit Paivays( unsigned int paiva=1, unsigned int kk=1, unsigned int vuosi=2013 ); 6

Olioiden purkaminen C# ja Java Roskienkeruu hoitaa purkamisen parhaaksi katsomaansa aikaan Olioiden "purkuvalmiutta" tarkastellaan olion saavutettavuuden avulla Javassa finalize, C# finalize/purkaja (C++:sta tuttu syntaksi vain hämäyksenä) C#:n purkaja, (unmanaged resources) Idisposable-olioiden hävittäminen (Dispose) Roskienkeruun pakkokäynnistys (yleensä turhaa/huono ajatus) onnistuu Collect-funktiolla Roskienkeruuesimerkki (vs. C++) 7

Kaksivaiheinen purkaminen Purkajan kutsuhetki epävarma Jos halutaan poistaa olio ohjelmasta, voidaan käyttää kaksivaiheista purkamista Kontrolloitu resurssien vapauttaminen erillisellä funktiokutsulla, kun oliosta halutaan eroon Roskienkeruun käyttämä purkaja/metodi, jonka ajamishetkellä ei merkitystä (hoitaa muistin siivouksen) 8

Java/C# vs. C++ Javassa roskienkeruun hoitaa JVM (Java Virtual Machine) C# (ja Windows ympäristö), roskienkeruusta vastaa CLR (Common Language Runtime) Molemmissa sama idea, rauta/käyttöliittymätason päällä oma hiekkalaatikko/ajoympäristö, jossa sovellukset suoritetaan C++ sovellus käännetään kullekin ympäristölle (natiivikoodia) Virtuaalikone vaihtuu kun vaihdetaan alustasta toiseen, sama ("käännetty") koodi toimii kaikkialla 9

Kopiorakentajat Kopiorakentaja: Luo uuden olion, joka on kopio jo olemassa olevasta oliosta Toimintona samanlainen kuin normaali olion luominen Kopiorakentaja on kuten normaali rakentaja, mutta ottaa alustuksessa käyttämänsä tiedot toiselta samanlaiselta oliolta Joissain tilanteissa pakollinen, joissain hyödyllinen C++: kopioita tarvitaan ja niitä syntyy automaattisesti mm. seuraavissa tilanteissa: Kun funktio palauttaa arvon(*) STL:n säiliöt Funktion parametrit ja arvonvälitys Lisäksi olioiden kopiointi on usein hyödyllistä myös ohjelmoijalle. (*)Return value optimization (RVO) kääntäjä voi käyttää paluuarvon optimointia ja poistaa tarpeettomia kopiointeja 10

Automaattisia kopioita string funktiopalauttaa() string paluu( "abc" ); // Tilapäinen kopio palautettavasta merkkijonosta luodaan, sillä funktion sisäisten muuttujien // näkyvyysalue päättyy funktion lopussa ja tällöin kaikki tilapäiset muuttujat poistetaan. return rval; void funktionparametri( string parametri ) // alkuperäisen sijaan funktion sisällä käsitellään kopiota // (call by value) parametri = "abc"; string alkuperainen( "original data" ); string kopio( alkuperainen ); // luodaan kopio ekplisiittisellä kopion rakentamisella funktiopalauttaa( alkuperainen ); vector< string > strings; strings.push_back( alkuperainen ); // kopio luodaan ja tallennetaan vectoriin string.push_back( kopio ); // Kun vektorin koko muuttuu, poistetaan muistissa ollut kopio ja luodaan kopion // kopio uuteen paikkaan kopion kopio lisätään vektorin perään. 11

Kopiointiin liittyviä kysymyksiä Milloin kopiointia tarvitaan? Mikä on olion kopio? Perusmuuttujat, kopioidaan alkuperäisen muuttujan muistin sisältö uudelle oliolle Olioiden kanssa on mietittävä mitä oikeastaan halutaan kopioida? Kaikkien jäsenmuuttujien sisältö? Kaikkien viitteiden ja osoittimien sisältö? Kaikki rekisteröinnit ja suhteet, joita oliolla on? 12

Mitä eri kopiointitapoja C++:ssa on? 13

Viitekopiointi Kopio luodaan luomalla uusi viite, joka osoittaa samaan olioon (Java, Smalltalk, C#) class CarOwner public: CarOwner( string name, Car* car ); CarOwner( string name, Car* car ); void changecar( Car* newcar ); private: string name_; Car* mycar_; ; class Car ; Car lada( lada ); CarOwner tero( tero, &lada ); CarOwner& ref1 = tero; CarOwner& ref2 = ref1; tero, ref1 ja ref2 viittaavat samaan olioon. Tero:CarOwner ref 1 ref 1 Tero changecar() drive() refuel() Tero:CarOwner Tero changecar() drive() refuel() ref 2 external data (Lada:Car) external data (Lada:Car) 14

Matala kopiointi Matala kopio luodaan kopioimalla kaikkien jäsenmuuttujien arvot. Kopiorakentaja on helppo luoda automaattisesti (kääntäjän toimesta) Ongelmat: luokan ulkopuoliset tiedot, pitääkö luoda kopio myös osoittimien (+viitteiden) päässä olevasta tiedosta, itse osoitin vai kenties jättää osoitin nolla-arvoon? Ennen: Jälkeen: Tero:CarOwner Tero f1() f2() f3() Tero:CarOwner Tero f1() f2() f3() Tero2:CarOwner Tero f1() f2() f3() external data (Lada:Car) external data (Lada:Car) Terosta tehdään klooni, tämän jälkeen Teron kopio kuvittelee voivansa ajella Teron autolla 15

Syvä kopiointi on hyvä kopiointi? Tehdään kopio myös olion tilaan kuuluvasta ulkopuolisesta datasta oikea kopiointitapa Mitkä osat olion ulkopuolisista asioista kuuluvat oliolle itselleen ja mitkä ovat rekisteröintejä tai jaettuja resursseja? Mitä olio omistaa ja mitä se käyttää? Kääntäjä ei voi päätellä näitä automaattisesti koodarin on määriteltävä nämä itse Class Circle private: int radius_; Color bordercolor_; Canvas* canvas_; Database* database_; Coordinate* location_; ; 16

Syväkopiointiesimerkki Ennen: Jälkeen: Tero:CarOwner Tero f1() f2() f3() Tero:CarOwner Tero f1() f2() f3() Tero2:CarOwner Tero f1() f2() f3() external data (Lada:Car) external data (Lada:Car) external data (Lada2:Car) Nyt Terolla ja kloonilla on molemmilla oma autonsa! 17

Periytyminen ja kopiorakentaja Kopiorakentaja on rakentaja (OMG?!?!½!) Aliluokan kopiorakentajan on kutsuttava kantaluokan kopiorakentajaa! Kantaluokan kopiorakentaja huolehtii kantaluokan kopiointiosuudesta, aliluokka omastaan Parametrina saatu olio annetaan kantaluokan kopiorakentajan parametriksi Miksi toimii/onnistuu? 18

Kopiorakentajat class Ihminen public: Ihminen( string n ); Ihminen( const Ihminen& i ); virtual ~Ihminen(); ; class Rallikuski: public Ihminen public: Rallikuski( string n, string a ); Rallikuski( const Rallikuski& r ); ; 19

toteutukset Ihminen::Ihminen( string n ): nimi_( n ) cout << "Ihminen " << nimi_ << " syntyi." << endl; Ihminen::Ihminen( const Ihminen& i ): nimi_( "klooni_" + i.nimi_ ) Ihminen::~Ihminen() cout << "Ihminen kuoleentui: " << nimi_ << endl; Rallikuski::Rallikuski( string n, string a ): Ihminen( n ), auto_( a ) Rallikuski::Rallikuski( const Rallikuski& r ): Ihminen( r ), auto_( r.auto_ ) cout << "rallikuskin kloonaus." << endl; 20

Mitäs täällä tapahtuukaan? Rallikuski-esimerkki laajennettuna (Rallikuski, Formulakuski ja SupKuski perittynä Ihmisestä) void haastattele( Ihminen kohde ) kohde.haastattelu(); Ihminen annaaakkosissaeka( Ihminen& vertailtava1, Ihminen& vertailtava2 ) return vertailtava1.nimi() >vertailtava2.nimi()? vertailtava1: vertailtava2; vector< Ihminen > ihmiskasa; Rallikuski mikko( "Mikko", "Ford" ); ihmiskasa.push_back( mikko ); ihmiskasa.push_back( SupKuski( "Yontza" ) ); for( Ihminen ihminen: ihmiskasa ) ihminen.haastattelu(); 21

Viipaloituminen C++:ssa oliota luotaessa on tiedettävä olion tyyppi (koodari kertoo, mitä haluaa luoda) Entäs jos halutaan kopioida kantaluokkaosoittimen päässä olevat oliot? Aliluokan olio on myös kantaluokan olio Mikä tahansa jälkeläinen kelpaa hyvin myös kantaluokan kopiorakentajalle Luodaan uusi kantaluokan olio kopiona aliluokan oliosta == viipaloituminen (slicing) 22

viipaloituminen Cat misse( "Misse" ); Animal elukka( misse ); Misse kelpaa hienosti kopiorakentajan parametriksi, mutta kopiointi koskee vain kantaluokan osuutta Aliluokan osuuteen ei kosketa ja se jää kopioitumatta => viipaloituminen Animal Kantaluokan kopiorakentaja Animal Cat Viipaloituminen! Going to be sliced 23

Viipaloitumispaikkoja Viipaloituminen voi tapahtua vahingossa(kin) useissa eri tilanteissa KirjastonKirja etsimyohassaoleva( const vector< KirjastonKirja* >& kirjat ); void kirjani( Kirja munkirja ) cout << Tää on mun kirja!" << endl; munkirja.tulosta( cout ); Paluuarvot ja funktion parametrit Kirjat klassikko("donkey Hot", "Mikuel de Servettes" ); klassikko = etsimyohassaoleva( kirjat ); KirjastonKirja lainattu( "Volter Kilpi", "Alastalon salissa", Date(1,1,2011 ) ); kirjani( lainattu ); vector< Ihminen > ihmiskasa; ihmiskasa.push_back( mikko ); ihmiskasa.push_back( SupKuski( "Yontza" ) ); Stl:n säiliöt Kun käsitellään olioita, joilla voi olla aliluokkia/perintähierarkioita, kannattaa vektoriin tuupata vain osoittimia 24

Viipaloituminen ja muut oliokielet Kopioimisviipaloituminen lähes yksinomaan C++:n ongelma Useimmissa muissa kielissä oliot aina viitteiden päässä olioiden elinkaari aina dynaaminen olio kopioidaan käskemällä alkuperäistä oliota kopioimaan itsensä Olio itse tietää oikean tyyppinsä, joten kopiointi onnistuu Arvoparametrit ja paluuarvot harvinaisia muissa kielissä (paitsi perustietotyypit) Muissa kielissä tietorakenteissa vain viitteitä olioihin 25

Kloonien tekeminen Miten kopioida kantaluokan päässä oleva olio, jonka tyyppi voi olla mitä vaan? Käydään läpi kaikki vaihtoehdot tyyppitarkastuksilla, kutsutaan oikeaa kopiorakentajaa kun tyyppi selvillä, ei ehkä kätevin vaihtoehto Entä jos/kun hierarkiaa laajennetaan/muokataan? 26

Kloonien tekeminen, kloonit Viipaloituminen kierrettävissä C++:ssa matkimalla muita kieliä Luokkiin jäsenfunktio kloonaa: Luo new:llä kopion oliosta(kopiorakentajaa käyttäen) Palauttaa osoittimen uuteen olioon Virtuaalifunktio, määritellään uudelleen jokaiseen aliluokkaan Kutsuttaessa luo aina oikeantyyppisen kopion olioista Ongelmia: Kopion tuhoamisvastuu siirtyy kutsujalle (delete) tai fiksujen osoittimien käyttö Joka luokkaan muistettava kirjoittaa oma kloonausfunktio, mitä käy jos unohdetaan? Muuta: (Paluuarvo-osoittimen tyyppi vaihtuu aliluokassa kovariantit paluutyypit) 27

kloonausfunktiot class Organism public: virtual ~Organism(); virtual Organism* clone() const = 0; ; class Animal: public Organism public: Animal( std::string id ); Animal( const Animal& a ); virtual ~Animal(); Location getlocation() const; std::string getid() const; virtual Animal* clone() const = 0; private: Location loc_; std::string id_; ; Kopiorakentaja voidaan pistää myös privatenäkyvyysalueelle, jolloin kopioita voi tehdä vain kloonaamalla, toimisiko? Miksei? Miksi esittely toistettu joka tasolla? class Bird: public Animal public: Bird( std::string id ); Bird( const Bird& ); virtual ~Bird(); virtual void sing() const; virtual void move( Location newl ) = 0; virtual void printvisited() const; virtual Bird* clone() const = 0; private: std::vector< Location* > visited_; ; class Crow: public Bird public: Crow( std::string id ); Crow( const Crow& c ); virtual ~Crow(); virtual void sing() const; virtual void move( Location newl ); virtual Crow* clone() const; ; class CarrionCrow: public Crow public: CarrionCrow( std::string id ); CarrionCrow( const CarrionCrow& cc ); virtual ~CarrionCrow(); virtual void sing() const; virtual CarrionCrow* clone() const; ; 28

Kloonauksen toteutus // toteutus vain tavallisille virtuaalifunktiolle (ei pure virtual) Crow* Crow::clone() const return new Crow( *this ); CarrionCrow* CarrionCrow::clone() const return new CarrionCrow( *this ); 29

#include "animals.hh" #include <iostream> using namespace std; void cloneall( vector<animal*>& zoo ); void moveanimals( vector<animal*>& animals ); käyttö int main() vector<animal*> birds; CarrionCrow* cr = new CarrionCrow( "Kalle" ); CarrionCrow* cr2 = cr->clone(); // kloonaamalla kopiointi CarrionCrow* cr3 = new CarrionCrow( *cr2 ); // kopiorakentajan kutsu cr->move( Location( 5, 6 ) ); cr->move( Location( 10, 2 ) ); birds.push_back( cr ); birds.push_back( cr2 ); birds.push_back( cr3 ); cloneall( birds ); moveanimals( birds ); cr->printvisited(); while(!birds.empty() ) delete birds.at( birds.size()-1 ); birds.pop_back(); 30

Käyttö, funktioiden toteutukset void cloneall( vector<animal*>& zoo ) unsigned originalsize = zoo.size(); for( unsigned i(0); i < originalsize; ++i ) cout << "hep" << endl; zoo.push_back( zoo.at(i)->clone() ); void moveanimals( vector<animal*>& animals ) for( unsigned i(0); i < animals.size(); ++i ) animals.at(i)->move( Location( animals.at(i)->getlocation().x+i, animals.at(i)->getlocation().y+i ) ); 31

Java ja kloonailu Javassa kloonattavissa olevat oliot tukevat clone() funktiota Tehdään kertomalla, että tietotyyppi toteuttaa Cloneable rajapinnan (erikoisrajapinta) Jos toteutusta ei määritellä, tekee vakiona matalakopioinnin (täälläkin koodari joutuu erikseen määrittelemään, jos halutaan syvä kopio) Javassakaan kopiointi/kloonaus ei ole välttämättä ongelmaton public class Viesti implements Cloneable private String sisalto; private Paivays pvm; public Viesti( String animi, Paivays apvm ) this.nimi = animi; this.pvm = apvm; public Object clone() throws CloneNotSupportedException Viesti clone = (Viesti)super.clone(); // kopioidaan myös viitteen päässä oleva Paivays-olio // (myös päiväyksen on oltava Cloneable) clone.pvm = (Paivays)pvm.clone(); return clone; 32

Periytyminen ja sijoitusoperaattori Sijoituksessa sama vastuujako kuin kopioinnissa: Aliluokan sijoitusoperaattori: kutsuu kantaluokan sijoitusta, sijoittaa aliluokkaosan Kantaluokan sijoitusoperaattori: sijoittaa kantaluokkaosan Aliluokan sijoitusoperaattorissa muistettava kutsua kantaluokan sijoitusoperaattoria! Kääntäjä ei varoita, jos kutsu unohtuu! (ja kantaluokan osa jää sijoittamatta) 33

Sijoitusoperaattorit class Organism public: virtual ~Organism(); virtual Organism* clone() const = 0; ; class Animal: public Organism public: Animal( std::string id ); Animal( const Animal& a ); Animal& operator =( const Animal& ); virtual ~Animal(); virtual void move( Location newl ) = 0; virtual std::string getid() const; Location getlocation() const; private: Location loc_; std::string id_; ; class Bird: public Animal public: Bird( std::string id ); Bird( const Bird& ); Bird& operator =( const Bird& ); virtual ~Bird(); virtual void sing() const; virtual void move( Location newl ) = 0; virtual void printvisited() const; private: std::vector< Location* > visited_; ; class Crow: public Bird public: Crow( std::string id ); Crow( const Crow& c ); Crow& operator =( const Crow& ); virtual ~Crow(); virtual void sing() const; virtual void move( Location newl ); ; class CarrionCrow: public Crow public: CarrionCrow( std::string id ); CarrionCrow( const CarrionCrow& cc ); CarrionCrow& operator =( const CarrionCrow& ); virtual ~CarrionCrow(); virtual void sing() const; ; 34

Toteutukset (Animal & Bird) Animal& Animal::operator =( const Animal& a ) if( this!= &a ) // kantaluokkakutsu varmuuden vuoksi, jos joskus laajennetaan Organism::operator =( a ); id_ = a.id_; loc_ = a.loc_; return *this; Bird& Bird::operator =( const Bird& b ) if( this!= &b ) Animal::operator =( b ); // tuhotaan vanhat while(!visited_.empty() ) delete visited_.at( visited_.size()-1 ); visited_.pop_back(); // kopioidaan kaikki sijoituskohteesta for( unsigned i(0); i < b.visited_.size(); ++i ) Location* loc = b.visited.at( i ); Location* newloc = new Location( *loc ); visited_.push_back( newloc ); return *this; 35

Toteutukset (Crow, CarrionCrow) Crow& Crow::operator =( const Crow& c ) if( this!= &c ) Bird::operator =( c ); return *this; CarrionCrow& CarrionCrow::operator =( const CarrionCrow& c ) if( this!= &c ) Crow::operator =( c ); return *this; 36

Viipaloituminen ja sijoitus Viipaloituminen vaarana myös sijoituksessa Aliluokan olio on kantaluokan olio sen voi sijoittaa kantaluokan olioon Kantaluokkaviitteiden ja -osoittimien kautta mahdollista myös aliluokkaolion sijoittaminen toisen aliluokan olioon! Sijoituksen viipaloituminen vaarana myös muissa oliokielissä (mutta niissä ei yleensä kieleen sisäänrakennettua oliosijoitusta) 37

class NinjaTurtle: public Animal public: NinjaTurtle(std::string id,std::string secretpower); NinjaTurtle( const NinjaTurtle& ); NinjaTurtle& operator =( const NinjaTurtle& ); void usepower() const; virtual std::string getid() const; virtual void move( Location newl ); NinjaTurtle* clone() const; private: std::string secretpower_; ; void assign( Animal& towhere, const Animal& from ) cout << "Before the assignment: " << endl; towhere.getinfo(); towhere = from; cout << "After the assignment: " << endl; towhere.getinfo(); int main() CarrionCrow cr( "Kalle" ); CarrionCrow* cr2 = cr->clone(); // kloonaamalla kopiointi NinjaTurtle raphael( "Raphael", "eat pizza" ); NinjaTurtle donatello( "Donatello", "consume pizza" ); assign( raphael, donatello ); assign( cr, *cr2 ); assign( donatello, cr ); Animal& donatelloref = donatello; donatelloref = cr; cr->printvisited(); delete cr2; // muistetaan poistaa dynaamisen kopioinnin luomat oliot return EXIT_SUCCESS; 38

Esimerkki sijoituksesta (elaintason viitteet) Korpista korppiin Korppi->ninjakilpikonna Elain Elain Elain Korppi viipaloituminen Korppi viipaloituminenninjakilpikonna loc1 loc2 loc3 loc4 Salainen kyky 39

Sijoitusviipaloitumisen estäminen Sijoitusoperaattori ei voi olla virtuaalinen: parametri ei ole sama aliluokassa! Muutamia(enemmän tai vähemmän) toimivia ratkaisuja: Estetään sijoitus, ei tuu viipaloitumisia Kaikki kantaluokat abstrakteja ei kantaluokkaolioita (sijoituksen voi silti tehdä, jos perityissä luokissa on määritelty sijoitusoperaattorit eikä niissä kutsuta kantaluokan sijoituksia) Sijoitettavan tyypin tarkastaminen ajoaikana typeid-operaattorilla 40

1 #include <typeinfo> 2 Korppi& Korppi::operator =(Korppi const& m) 3 4 if(typeid(*this)!= typeid(korppi) typeid(m)!= typeid(korppi)) 5 6 /* Virhetoiminta, esim. Poikkeus */ 7 8 // Tai assert(typeid(*this)== typeid(korppi) && typeid(m) == 9 // typeid(korppi)); 10 if(this!= &m) 11... // normaali sijoitus 12 41

Kloonaaminen ja kloonattavuuden tarkistus Miksi kloonaus toimii, mutta virtuaalinen sijoitusoperaattori ei? Paluuarvo-osoittimen tyyppi vaihtuu aliluokassa kovariantit paluutyypit Sama ei sallittua funktion parametreille 42

Yhteenvetoa Kopiointi ja sijoittaminen muuttuvat haasteellisemmiksi perinnän astuessa mukaan kuvioihin Viipaloituminen (kopio, sijoitus) Dynaaminen kloonaaminen toimii, jos olion tyyppistä ei ole tietoa 43