Noudata käytäntöä, että kullekin luokalle luodaan oma header-tiedosto ja oma cpp-tiedosto, joilla on sama nimi kuin luokalla. Tämä hoituu automaattisesti, kun luot luokat Qt-Creatorin toiminnolla "Add New -> C++ Class". Tutustu aluksi UML-kaavion symbooleihin kts. https://peatutor.com/cplus/index.php#uml
Luo C++-projekti nimeltään h3b
Animal, joka sisältää virtuaalisen metodin callOut. Tämä metodi tulostaa tekstin "Eläin ääntelee."Dog, joka ylikirjoittaa metodin callOut. Ylikirjoitetun metodin tulisi tulostaa teksti "Koira haukkuu!"main-funktio, jossa luodaan Animal-luokan olio ja Dog-luokan olio.callOut-metodia ja varmista, että oikea viesti tulostuu. Kun sinulla on C++-ohjelmassa, perintäsuhde luokkien Animal ja Dog välillä, voit luoda ja käsitellä olion joko kantaluokan (Animal) tai perivänluokan (Dog) tyypin kautta. Molemmilla tavoilla on etunsa. Tarkastellaan ensin molempia tapoja:
Huom! Modernissa C++:ssa käytetään älykkäitä osoittimia (smart pointers) kuten unique_ptr tai shared_ptr raakaosoittimien sijaan. Näin muisti vapautuu automaattisesti eikä delete-komentoa tarvita.
unique_ptr<Animal> dog = make_unique<Dog>(); (Polymorfismi)unique_ptr<Dog> dog = make_unique<Dog>(); (Perivänluokan käyttö suoraan)Plolymorfinen olio voidaan luoda lauseella: unique_ptr<Animal> dog = make_unique<Dog>();, jolloin käytetään kantaluokan tyyppiä, mutta luodaan perivänluokan olio. Tämä tarjoaa monia etuja:
Huom! Tässä tapauksessa Animal luokan destruktori on määritettävä virtuaaliseksi.
Voit käsitellä Dog-oliota yleisemmällä Animal-tyypillä. Tämä mahdollistaa koodin kirjoittamisen siten, että se voi käsitellä mitä tahansa Animal-tyyppistä oliota (Dog, Cat, jne.), mikä lisää joustavuutta ja laajennettavuutta.
#include <memory>
#include <vector>
std::vector<std::unique_ptr<Animal>> animals;
animals.push_back(std::make_unique<Dog>());
animals.push_back(std::make_unique<Cat>());
animals.push_back(std::make_unique<Bird>());
for (const auto& animal : animals) {
animal->callOut(); // Jokainen eläin toteuttaa oman luokkansa callOut-metodin
}
// Muisti vapautuu automaattisesti, kun animals-vektori tuhoutuu
Funktiot voivat käsitellä Animal-tyyppisiä olioita ilman, että niiden tarvitsee tietää perivää luokkaa:
void handleAnimal(const std::unique_ptr<Animal>& animal) {
animal->callOut(); // Polymorfinen kutsu
}
Voit lisätä uusia eläinlajeja (Cat, Bird, jne.) ilman, että sinun tarvitsee muuttaa vanhaa koodia, joka käyttää kantaluokkaa (Animal).
unique_ptr<Dog> dog = make_unique<Dog>();, jolloin käytetään perivänluokan tyyppiä. Tämä voi olla hyödyllistä seuraavissa tilanteissa:
Jos haluat käyttää Dog-luokan erityisiä metodeja tai jäsenmuuttujia, jotka eivät ole kantaluokassa, sinun täytyy käyttää Dog-tyyppiä:
auto dog = std::make_unique<Dog>();
dog->getOwner(); // Ominaisuus, joka on vain Dog-luokassa, ei Animal-luokassa
Jos et tarvitse polymorfismia, suora Dog-osoitin voi olla tehokkaampi, koska se ei aiheuta ylimääräistä suorituskykykustannusta virtuaalifunktiotaulun (vtable) käytöstä (vtable on taulukko, joka sisältää osoittimet virtuaalisiin metodeihin).
| Ominaisuus | unique_ptr<Animal> |
unique_ptr<Dog> |
|---|---|---|
| Polymorfismi | Kyllä | Ei |
| Koodin joustavuus ja laajennettavuus | Kyllä | Ei |
| Perivänluokan metodit käytettävissä | Ei ilman tyyppimuunnosta | Kyllä |
| Suorituskyky | Hieman hitaampi (virtuaalitaulu) | Hieman nopeampi |
| Muistinhallinta | Automaattinen | Automaattinen |
unique_ptr<Animal> ja milloin unique_ptr<Dog>?unique_ptr<Animal>, kun haluat hyödyntää polymorfismia ja käsitellä erilaisia eläinluokkia samalla tavalla.unique_ptr<Dog>, kun tiedät tarkalleen, että työskentelet vain Dog-olioiden kanssa ja haluat käyttää sen erityisiä metodeja.std::unique_ptr<Animal> animal = std::make_unique<Dog>();
animal->callOut(); // Kutsuu Dog:n toteuttamaa callOut-metodia
auto dog = std::make_unique<Dog>();
dog->getOwner(); // Erityinen Dog-metodi