2. díl - První objektová aplikace v C++

C++ Objektově orientované programování První objektová aplikace v C++

Na začátku sekce se základními strukturami jazyka C++ jsme si vytvořili program Hello world. Udělejme si nyní podobný program jako úvod do objektově orientovaného programování. Naprogramujme si Hello object world!

Nejprve si vytvoříme nový projekt (konzolovou aplikaci). Vytvoříme soubor Source.cpp, do kterého si můžeme napsat nějaký základ kódu:

#include <conio.h>
#include <windows.h>
#include <locale>

int main(void)
{
        //nastavíme české kódování(podpora diakritiky v konzoli)
        std::locale loc("Czech_Czech Republic.1250");
        std::locale::global(loc);
        //nastavíme titulek okna
        SetConsoleTitleA("Hello object world!");

        return 0;
}

V Solution Exploreru napravo klikneme pravým tlačítkem myši na složku Source files a vybereme Add -> Class.

Přidání C++ třídy ve Visual Studio

V okně, které se nám zobrazí, rozklikneme C++ Class. Pak se zobrazí další okno, ve kterém vyplníme údaje podle obrázku:

Přidání C++ třídy ve Visual Studio

Visual studio nám vytvoří dva soubory: zdravic.h (tam bude samotná třída) a zdravic.cpp (kde budou definice funkcí). Ty vypadají takto:

//zdravic.h
#pragma once
class zdravic
{
public:
        zdravic(void);
        ~zdravic(void);
};
//zdravic.cpp
#include "zdravic.h"

zdravic::zdravic(void)
{
}

zdravic::~zdravic(void)
{
}

#pragma once zajišťuje, že bude soubor čten jen jednou zdravic(void) a ~zdravic(void) jsou tzv. konstruktor a destruktor. Konstruktor se spustí při vytvoření instance třídy, destruktor ve chvíli, kdy na ni ztratíme referenci. Až budeme tuto třídu používat, budeme chtít, aby něco uměla. Kvůli tomu si třídu zdravic rozšíříme o nějakou metodu. Ta se v C++ zapisuje stejně jako funkce:

//zdravic.h
#pragma once
class zdravic
{
public:
        void pozdrav();
        zdravic(void);
        ~zdravic(void);
};
//zdravic.cpp
#include <iostream>
using namespace std;
#include "zdravic.h"

zdravic::zdravic(void)
{
}

zdravic::~zdravic(void)
{
}

void zdravic::pozdrav(void)
{
        cout << "Hello object world!";
}

Zde jsme prozatím skončili, přejdeme do Source.cpp.

Nyní si v těle metody Main vytvoříme instanci třídy zdravic. Bude to tedy ten objekt zdravič, se kterým budeme pracovat. Objekty se ukládají do proměnných, název třídy slouží jako datový typ. Instance můžeme pojmenovat jakkoliv, já doporučuji, aby v názvu byl "schovaný" název třídy. Deklarujme si tedy proměnnou a následně v ní založme novou instanci třídy zdravic:

zdravic _zdravic;

Tím se vytvoří proměnná _zdravic a zavolá se konstruktor třídy zdravic - zdravic(void). Instanci můžeme vytvořit i takto:

zdravic* _zdravic = new zdravic();

Tento způsob vytvoří instanci dynamicky. Vrátíme se k němu v pozdějších dílech seriálu.

A protože už máme vytvořený svůj první objekt (instanci), můžeme zavolat jeho metodu. Na konec kódu (před return 0;) ještě doplníme _getch();(funkce ze souboru conio.h, aby program hned neskončil a čekal na stisk libovolné klávesy). Soubor Source.cpp tedy vypadá takto:

#include "zdravic.h"
#include <conio.h>
#include <windows.h>
#include <locale>

int main(void)
{
        //nastavíme české kódování(podpora diakritiky v konzoli)
        std::locale loc("Czech_Czech Republic.1250");
        std::locale::global(loc);
        //nastavíme titulek okna
        SetConsoleTitleA("Hello object world!");

        zdravic _zdravic;
        _zdravic.pozdrav();

        _getch();
        return 0;
}

Teď můžeme program spustit.

První objektová aplikace v C++

Máme tedy svou první objektovou aplikaci!

Dejme nyní naší metodě Pozdrav() parametr jmeno, aby dokázala pozdravit konkrétního uživatele:

//zdravic.h
void pozdrav(string jmeno);
//zdravic.cpp
void zdravic::pozdrav(string jmeno)
{
        cout << "Ahoj uživateli " << jmeno.c_str() << endl;
}

Nyní upravíme naši metodu Main:

zdravic _zdravic;
_zdravic.pozdrav("Karel");
_zdravic.pozdrav("Petr");
_getch();
Metoda s parametrem v C++

Třídě nyní přidáme nějakou datovou složku, nabízí se text, kde bude uložen text pozdravu. Datové složky (atributy) se definují stejně, jako proměnné. Upravme tedy naši třídu:

//zdravic.h
class zdravic
{
public:
        string text;
        void pozdrav(string jmeno);
        zdravic(void);
        ~zdravic(void);
};
//zdravic.cpp
void zdravic::pozdrav(string jmeno)
{
        cout << text.c_str() << ' ' << jmeno.c_str() << '!' << endl;
}

Text nyní musíme pochopitelně nastavit vytvořené instanci v Source.cpp:

zdravic _zdravic;
_zdravic.text = "Ahoj uživateli";
_zdravic.pozdrav("Karel");
_zdravic.pozdrav("Petr");
_zdravic.text = "Vítam tě tu programátore";
_zdravic.pozdrav("Richard");
Atributy v C++

Vzhledem k objektovému návrhu není nejvhodnější, aby si každý objekt ovlivňoval vstup a výstup, jak se mu zachce. Pochopitelně narážím na naše vypisování do konzole. Každý objekt by měl mít určitou kompetenci a tu by neměl překračovat. Pověřme náš objekt pouze sestavením pozdravu a jeho výpis si zpracujeme již mimo, v našem případě v metodě Main. Výhodou takto navrženého objektu je vysoká univerzálnost a znovupoužitelnost. Objekt doposud umí jen psát do konzole, my ho však přizpůsobíme tak, aby daná metoda text pouze vracela a bylo na jeho příjemci, jak s ním naloží. Takto můžeme pozdravy ukládat do souborů, psát na webové stránky nebo dále zpracovávat.

Jelikož chceme, aby metoda vracela hodnotu a to string, změníme její dosavadní typ void na string. K návratu hodnoty použijeme příkaz return. Upravme kód třídy:

//zdravic.h
#pragma once
#include <iostream>
using namespace std;

class zdravic
{
public:
        string pozdrav(string jmeno);
        zdravic(void);
        ~zdravic(void);
        string text;
};
//zdravic.cpp
#include "zdravic.h"

zdravic::zdravic(void){}

zdravic::~zdravic(void){}

string zdravic::pozdrav(string jmeno)
{
        string str = text;
        str += ' ';
        str += jmeno;
        str += "!\n";
        return str;
}

K tomu musíme upravit soubor Source.cpp:

#include "zdravic.h"
#include <conio.h>
#include <windows.h>
#include <iostream>
#include <locale>
using namespace std;

int main(void)
{
        locale loc("Czech_Czech Republic.1250");
        locale::global(loc);
        SetConsoleTitleA("Hello object world!");

        zdravic _zdravic;
        _zdravic.text = "Ahoj uživateli";
        cout << _zdravic.pozdrav("Karel");
        cout << _zdravic.pozdrav("Petr");
        _zdravic.text = "Vítám tě tu programátore";
        cout << _zdravic.pozdrav("Richard");

        _getch();
        return 0;
}

Nyní je náš kód dle dobrých praktik. Ještě naši třídu okomentujme, jak se sluší a patří. Komentáře budeme psát nad název třídy a nad název každého atributu a metody. K jejich zápisu použijeme trojlomítko "///". Vyplatí se nám to ve chvíli, když na třídě používáme nějakou metodu, její popis se nám zobrazí v našeptávači. Zdokumentovaná třída může vypadat např. takto:

#pragma once
#include <iostream>
using namespace std;

/// Třída reprezentuje zdravič, který slouží ke zdravení uživatelů
class zdravic
{
public:
        /// Text pozdravu
        string text;
        /// Pozdraví uživatele textem pozdravu a jeho jménem
        string pozdrav(string jmeno);
        zdravic(void);
        ~zdravic(void);
};

Podíváme se, že nám VS opravdu popisky zobrazí:

Visual Studio zobrazující C++ dokumentaci

A jsme u konce. Námi napsaný program má již nějakou úroveň, i když vlastně nic nedělá. Za úkol máte předělat si naši konzolovou kalkulačku do objektů. Příště si uděláme takovou jednoduchou hříčku, necháme dva objekty (bojovníky) soupeřit v aréně (také objektu). Máte se na co těšit ;-)


 

Stáhnout

Staženo 331x (19.9 MB)
Aplikace je včetně zdrojových kódů v jazyce C++

 

  Aktivity (3)

Článek pro vás napsal Zdeněk Pavlátka
Avatar
Autor se věnuje spoustě zajímavých věcí :)

Jak se ti líbí článek?
Celkem (9 hlasů) :
3.444443.444443.44444 3.444443.44444


 



 

 

Komentáře
Zobrazit starší komentáře (24)

Avatar
Libor Šimo (libcosenior):

Napísal som to a poprosím vás o kritiku. OOP je pre mňa stále neprebádané územie. Prosím vychádzajte z toho, že mám za sebou len túto lekciu.

/** vypocet.h */
#ifndef VYPOCET_H
#define VYPOCET_H

#include <iostream>
#include <cstdio>
#include <sstream>

using namespace std;

/** Trieda vypocita zadanu matematicku operaciu a vypise vysledok */
class vypocet
{
    public:
        /**
        * Funkcia vyziada od uzivatela string (cislo), prekonvertuje ho na float
        * s osetrenim vstupu a vrati float
        * @param poradove cislo vstupu, typ int
        * @return premenna typu float
        */
        float StringToFloat(int pc);
        /**
        * Funkcia vytvori instanciu triedy vypocet, vypyta od uzivatela vstupne cisla
        * s pouzitim metody StringToFloat(), vypyta od uzivatela vyber matematickej
        * operacie, vypocita zadanu matematicku operaciu a vypise vysledok.
        * To cele v cykle s moznostou zadania noveho prikladu a nieco je blbuvzdorne
        */
        void VypocetMatematickejOperacie();
        vypocet();
        ~vypocet();
    protected:
    private:
};

#endif // VYPOCET_H
/** vypocet.cpp */
#include "vypocet.h"

vypocet::vypocet() {}
vypocet::~vypocet() {}

float vypocet::StringToFloat(int pc)
{
    float a;
    string str;
    stringstream *stream = 0;
    char c;
    do {
        delete stream;
        cout << "Zadajte " << pc << ". cislo: ";
        cin >> str;
        stream = new stringstream(str);
        *stream >> a;
    } while(stream->fail() || stream->get(c));
    delete stream;

    return a;
}

void vypocet::VypocetMatematickejOperacie()
{
    vypocet _vypocet;
    cout << "Vitajte v kalkulacke" << endl;

    bool pokracovat = true;
    while (pokracovat) {
        putchar('\n');
        float a = _vypocet.StringToFloat(1);
        float b = _vypocet.StringToFloat(2);
        putchar('\n');
        cout << "Zvolte operaciu:" << endl;
        cout << "1 - scitanie" << endl;
        cout << "2 - odcitanie" << endl;
        cout << "3 - nasobenie" << endl;
        cout << "4 - delenie" << endl;

        int volba;
        cin >> volba;

        float vysledok = 0;
        bool platnaVolba = true;
        switch (volba) {
            case 1:
                vysledok = a + b;
                break;
            case 2:
                vysledok = a - b;
                break;
            case 3:
                vysledok = a * b;
                break;
            case 4:
                vysledok = a / b;
                break;
            default:
                platnaVolba = false;
                break;
        }
        if (platnaVolba)
            cout << "Vysledok: " << vysledok << endl;
        else
            cout << "Neplatna volba" << endl;

        cout << "Dalsi priklad? [a/n]" << endl;
        char c;
        platnaVolba = false;
        while (!platnaVolba) {
            fflush(stdin);
            c = getchar();
            switch (tolower(c)) {
                case 'a':
                    pokracovat = true;
                    platnaVolba = true;
                    break;
                case 'n':
                    pokracovat = false;
                    platnaVolba = true;
                    break;
                default:
                    cout << "Neplatna volba, zadajte prosím a/n" << endl;
                    break;
            }
        }
    }
}
/** main.cpp */
#include "vypocet.h"

int main(void)
{
    vypocet _vypocet;
    _vypocet.VypocetMatematickejOperacie();

    return 0;
}
Editováno 13.3.2015 11:53
Odpovědět 13.3.2015 11:51
Aj tisícmíľová cesta musí začať jednoduchým krokom.
Avatar
Libor Šimo (libcosenior):

Predsa len som sa zamyslel a uvedomil som si, že program treba rozdeliť do malých častí, ktoré sú znovu použiteľné. Tak som to napísal tak.
Môže sa na to prosím niekto pozrieť, či som to neprehnal?
http://skolka-jazyka-c.freespace.sk/viewtopic.php?…

Odpovědět 14.3.2015 11:34
Aj tisícmíľová cesta musí začať jednoduchým krokom.
Avatar
Jaroslav Hořejší:

Mám problém, když nápiši tvůj příklad se Ahoj uživatel jmeno, při kompilaci se objeví chyba c2061. Jak ji mám odstranit?

 
Odpovědět 1. února 21:32
Avatar
Odpovědět  +1 1. února 23:38
I have a charger. I have Note 7. Umh I haven't Note7.
Avatar
Jaroslav Hořejší:
/** zdravic.h */
#pragma once
class Zdravic
{
public:
        void pozdrav(string jmeno);
        Zdravic();
        ~Zdravic();
};

/** zdravic.cpp */
#include "stdafx.h"
#include "Zdravic.h"
#include <iostream>
#include <cstring>

using namespace std;

Zdravic::Zdravic()
{
}


Zdravic::~Zdravic()
{
}
void Zdravic::pozdrav(string jmeno)
{
        cout << "Ahoj uživateli" << jmeno.c_str() << endl;
}
/**objektova aplikace */
#include "stdafx.h"
#include "Zdravic.h"
#include <conio.h>
#include <Windows.h>
#include <locale>

using namespace std;

int _tmain(int argc, _TCHAR* argv[])
{
        //nastavíme české kódování(podpora diakritiky v konzoli)
        locale loc("Czech_Czech Republic.1250");
        locale::global(loc);
        //nastavíme titulek okna
        SetConsoleTitleA("Hello object world!");
        Zdravic zdravic;
        zdravic.pozdrav("Karel");
        _getch();
        return 0;
}
 
Odpovědět 2. února 11:16
Avatar
Emilia T
Člen
Avatar
Emilia T:

informacia o volani destruktora nie je korektna "destruktor ve chvíli, kdy na ni ztratíme referenci." Toto nie je pravda, ked stratime referenciu na objekt tak objekt dalej existuje, zabera pamat preto je v c++ velmi dolezite pamatat si objekty a ked dany objekt uz dalej nie je potrebny treba zavolat destruktor.

 
Odpovědět 10. února 21:18
Avatar
Filip Šohajek
Redaktor
Avatar
Odpovídá na Emilia T
Filip Šohajek:

Toto je pravda, jakmile odejde ze scopu, zavolá se jeho destruktor.

 
Odpovědět 10. února 22:26
Avatar
Polymath
Člen
Avatar
Odpovídá na Emilia T
Polymath:

Platí to pro instance na zásobníku. A destruktor se volat nedá, provede se automaticky při rušení objektu, k čemuž pro instance na haldě slouží delete.

 
Odpovědět 11. února 14:56
Avatar
Emilia T
Člen
Avatar
Emilia T:

mozno som sa nevyjadrila dostatocne presne, destruktor sa vola vtedy ked je objekt "destroyed (deallocated)". nie vtedy ked nan stratime referenciu. Pokial je objekt vytvoreny dynamickym alokovanim pamate, je potrebne pamat dealokovat teda zavolat na prislusny objekt delete operator.

https://msdn.microsoft.com/…t4fe76c.aspx

 
Odpovědět 11. února 18:25
Avatar
Emilia T
Člen
Avatar
Odpovídá na Emilia T
Emilia T:

neviem prist na to ako sa tu upravuju komentare... namiesto vety "nie vtedy ked nan stratime referenciu" malo byt "neda sa vo vseobecnosti povedat ze to nastane vtedy ked nan stratime referenciu"

 
Odpovědět 11. února 18:38
Děláme co je v našich silách, aby byly zdejší diskuze co nejkvalitnější. Proto do nich také mohou přispívat pouze registrovaní členové. Pro zapojení do diskuze se přihlas. Pokud ještě nemáš účet, zaregistruj se, je to zdarma.

Zobrazeno 10 zpráv z 34. Zobrazit vše