Luo luokka Car, jolla on seuraavat yksityiset attribuutit: string make, string model ja int year. Kirjoita konstruktori, joka alustaa nämä attribuutit, ja metodi void display(), joka tulostaa auton tiedot.
Muokkaa Car-luokkaa edellisestä tehtävästä lisäämällä getter- ja setter-metodit jokaiselle attribuutille. Varmista, että asetettu data on validia (esim. vuoden tulee olla suurempi kuin 1885).
Lisää Car-luokkaan ylikuormitettu konstruktori, joka ottaa parametreina vain make ja model. Tässä tapauksessa year saa oletusarvon 2020. Käytä tätä ylikuormitettua konstruktoria olioiden luomiseen.
Luo luokka ElectricCar, joka perii Car-luokan. Lisää yksityinen attribuutti int batteryCapacity ja metodi void charge(), joka tulostaa viestin, että auto latautuu.
Ylikirjoita ElectricCar-luokassa display()-metodi niin, että se sisältää myös akun kapasiteetin tiedot. Luo Car-olioista koostuva vektori, joka sisältää sekä Car- että ElectricCar-olioita. Käytä polymorfismia kutsuaksesi display()-metodia jokaiselle oliolle.
OHJE: Polymorfismi vaatii osoittimia tai viittauksia! Käytä unique_ptr:ää:
vector<unique_ptr<Car>> carList;
carList.push_back(make_unique<Car>());
carList.push_back(make_unique<ElectricCar>());
for (const auto& obj : carList) {
obj->display(); // Kutsuu oikean luokan display():ta (polymorfismi)
}
Miksi ei vector<Car>? Jos tallennat olioita suoraan (ei osoittimia), tapahtuu object slicing:
ElectricCar-olio "leikataan" → vain Car-osa jää jäljelleauto antaa kääntäjän päätellä tyypin automaattisesti. Tässä obj on tyyppiä const unique_ptr<Car>&.
Tee Car-luokan display()-metodista virtuaalinen (virtual), ja havainnollista miten virtuaaliset funktiot mahdollistavat ajoaikaisen polymorfismin.
Tapa 1: Käytä unique_ptr:ää kantaluokan osoittimena, joka osoittaa johdettuun olioon:
unique_ptr<Car> objectCar = make_unique<ElectricCar>(); objectCar->display(); // Kutsuu ElectricCar::display() jos virtual
Tapa 2: Jos haluat käyttää pinomuistissa olevia olioita, voit käyttää viitettä:
ElectricCar objectElectricCar; Car& carRef = objectElectricCar; carRef.display(); // Kutsuu ElectricCar::display() jos virtual
Tehtävä: Testaa molempia tapoja ja katso, mikä ero on kun display() on virtual vs. ei-virtual.
Luo abstrakti luokka Vehicle, jolla on puhdas virtuaalifunktio void start() = 0. Peri tästä luokasta Car- ja Motorcycle-luokat. Toteuta start()-metodi molemmissa johdetuissa luokissa.
Testaa polymorfismia: Luo vector<unique_ptr<Vehicle>> ja lisää siihen sekä Car- että Motorcycle-olioita. Käy läpi vektori ja kutsu start() jokaiselle ajoneuvololle.
Muistutus: Abstraktista luokasta ei voi luoda olioita suoraan - vain perivistä luokista!
Luo luokka HybridCar, joka perii sekä Car-luokan että toisen luokan nimeltä FuelCar. Lisää FuelCar-luokkaan attribuutti int fuelTankCapacity ja havainnollista, miten moniperintä toimii C++:ssa.
Toteuta Car-luokkaan destruktori, joka tulostaa viestin (esim. auton mallin), kun Car-olio tuhotaan. Havainnollista destruktorien kutsumista:
{ Car obj; } ja katso milloin destruktori kutsutaan.unique_ptr<Car> ja katso milloin destruktori kutsutaan kun unique_ptr poistuu scopesta.vector<unique_ptr<Car>>, lisää autoja ja katso milloin ne tuhoutuvat.Huom: Lisää myös konstruktoriin cout-tulostus, jotta näet luomis- ja tuhoutumisjärjestyksen!
Luo kaksi luokkaa: Book ja Library. Book-luokalla tulee olla:
string-attribuutit: title ja author.display(), joka tulostaa kirjan tiedot.
Library-luokan tulee:
vector<unique_ptr<Book>> kirjojen tallentamiseen (kompositio).addBook(string title, string author), joka luo uuden kirjan ja lisää sen vektoriin.displayBooks(), joka tulostaa kaikki kirjat.unique_ptr hoitaa sen automaattisesti!
Tehtävä: Toteuta molemmat luokat ja testaa ne luomalla Library-olio, lisäämällä kirjoja ja tulostamalla ne.
Vihje: Käytä push_back(make_unique<Book>(...)) kirjojen lisäämiseen vektoriin.
Luo kolme luokkaa: Screen, Keyboard ja Laptop.
Screen-luokalla tulee olla:
float-attribuutti size (tuumina).display(), joka tulostaa näytön koon.Keyboard-luokalla tulee olla:
string-attribuutti layout (esim. "QWERTY").display(), joka tulostaa näppäimistön asettelun.Laptop-luokalla tulee olla:
unique_ptr<Screen> jäsenenä (kompositio - laptop omistaa näytön).unique_ptr<Keyboard> jäsenenä (kompositio - laptop omistaa näppäimistön).make_unique:lla.display(), joka tulostaa näytön ja näppäimistön tiedot.
Tehtävä: Toteuta luokat ja testaa ne luomalla Laptop-olio ja kutsumalla sen display()-metodia.
Huom: Tämä on kompositio, koska Laptop omistaa näytön ja näppäimistön. Kun laptop tuhoutuu, myös näyttö ja näppäimistö tuhoutuvat automaattisesti.
Luo kolme luokkaa: Address, Person ja Company.
Address-luokalla tulee olla:
string-attribuutit: street ja city.display() osoitteen tulostamiseen.Person-luokalla tulee olla:
string-attribuutti name.unique_ptr<Address> jäsenenä (kompositio - henkilö omistaa osoitteen).make_unique:lla.display(), joka tulostaa henkilön nimen ja osoitteen.Company-luokalla tulee olla:
string-attribuutti companyName.unique_ptr<Person> jäsenenä (kompositio - yritys omistaa toimitusjohtajan).make_unique:lla.display(), joka tulostaa yrityksen nimen ja toimitusjohtajan tiedot.
Tehtävä: Toteuta luokat ja testaa ne luomalla Company-olio ja kutsumalla sen display()-metodia.
Sisäkkäinen kompositio (Nested Composition) tarkoittaa:
Company omistaa Person-olion (kompositio)Person omistaa Address-olion (kompositio)Company tuhoutuu → Person tuhoutuu → Address tuhoutuu (ketjureaktio!)Lisää destruktoreihin cout-tulosteet, jotta näet tuhoutumisjärjestyksen!
Luo Car-luokkaan metodi void drive(int distance), joka heittää poikkeuksen, jos distance on negatiivinen. Käytä try- ja catch-lohkoja tämän poikkeuksen käsittelemiseksi pääfunktiossa.
Huom! Friend-funktio ei kuulu kurssin sisältöön. Tässä lyhyesti siitä:
friend-avainsanalla luokan sisällä, mutta sitä ei määritellä luokan jäsenfunktioksi.Kirjoita friend-funktio compareCars(), joka vertaa kahden Car-olion year-attribuuttia ja palauttaa uudemman auton. Havainnollista friend-funktion käyttö pääfunktiossa.