-
Pokud jsme doposud v našich programech pracovali se soubory, pak to bylo
způsobem, který se používal v jazyku C. Je nutno se také seznámit se čtením
a zápisem souborů pomocí datových proudů. Základní souborové vstupně/výstupní
operace jsou v C++ prováděny pomocí těchto tříd:
-
třída ofstream provádí výstup do souboru,
-
třída ifstream provádí vstup ze souboru,
-
třída fstream provádí jak vstup, tak i výstup souborů.
Následující jednoduchá konzolová aplikace čte svůj vlastní zdrojový soubor
a zobrazuje jej na obrazovce (program uložíme do CTENISOUBORU.CPP). Pokud
program uložíme pod jiným jménem, pak nebude správně pracovat.
#include <condefs.h>
#include <fstream.h>
#include <conio.h>
#pragma hdrstop
#pragma argsused
int main(int argc,
char **argv)
{
char buff[80];
ifstream vstsoubor;
vstsoubor.open("ctenisouboru.cpp");
if (!vstsoubor)
return 0;
while (!vstsoubor.eof()){
vstsoubor.getline(buff, sizeof(buff));
cout << buff << endl;
}
vstsoubor.close();
getch();
return 0;
}
Na začátku programu vytváříme instanci třídy ifstream nazvanou
vstsoubor.
Na dalším řádku otvíráme náš soubor pro vstup. Následuje test úspěšnosti
otevření souboru (v případě neúspěchu je program ukončen). Podmínkou ukončení
cyklu je volání metody eof třídy ifstream. Tato metoda vrací
true
při nalezení konce souboru. Jednotlivé řádky souboru jsou čteny metodou
getline.
Text řádku je umístěn do znakového pole buff a potom je vypsán na
obrazovku. Po dočtení souboru je soubor ještě uzavřen (metoda
close).
Pro používání třídy ifstream je nutno vložit hlavičkový soubor FSTREAM.H.
Jeden z konstruktorů ifstream přebírá jako parametr ukazazel
na char a umožňuje tak zadat jméno souboru při vytváření instance
třídy. Pomocí tohoto konstruktoru lze nahradit červené řádky z předchozího
programu následujícím řádkem:
ifstream vstsoubor("ctenisouboru.cpp");
Když vytvoříme objekt tímto způsobem, pak použití metody open
již není nutné, neboť soubor je automaticky otevřen konstruktorem. Nezapoměňte
při zadávání specifikace souboru i s adresářevou cestou nahrazovat znaky
\ pomocí \\.
Uzavření souboru není nutné. Destruktor ifstream testuje, zda
soubor zůstal otevřen a pokud ano, pak jej před zrušením instance třídy
uzavře. Používání close tedy není vyžadováno. Pokud jej použijeme,
pak tím naznačujeme, že soubor již dále není zapotřebí.
-
V našem programu je malá chyba. Program vypíše před ukončením cyklu jeden
prázdný řádek navíc. Abychom tuto chybu odstranili, musíme náš cyklus zapsat
takto:
while (!vstsoubor.getline(buff,
sizeof(buff)).eof()){
cout <<
buff << endl;
}
Zřetězení metod je dovoleno, i když není moc používáno. Jeho použitím
získáme méně srozumitelný kód. Umožňuje ale snadno odstranit některé chyby.
-
Protože třídy zpracovávající soubory jsou odvozeny od iostream,
můžeme také používat operátory vložení a výběru do/z datového proudu a
to stejně jako u datových proudů cin a cout. V našem programu
jsme použili getline, protože >> ukončí čtení po nalezení první
mezery (nebo odřádkování).
Operátor >> je výhodný při čtení jednotlivých hodnot. Např. následující
část programu čte celá čísla ze souboru a zobrazuje je na obrazovce.
ifstream vstsoubor("nejakysoubor.dat");
while (!vstsoubor.eof()){
int x;
vstsoubor
>> x;
cout <<
x << endl;
}
Operátor výběru z proudu čte data z textového souboru ale ne z binárního
souboru. Tento operátor čte text ze souboru a převádí jej na číslo.
-
Vytváření souborů je snadnější než jejich čtení. Namísto vytvoření instance
třídy ifstream, vytvoříme instanci třídy ofstream a pro zápis
do souboru používáme operátor vložení do proudu. Např.
ofstream vystsoubor("Test.dat");
if (!vystsoubor)
return 0;
for (int i = 0; i
< 10; i++) {
vystsoubor
<< "Toto je řádek " << (i + 1) << endl;
}
vystsoubor.close();
-
Vytvořte konzolovou aplikaci, která bude přebírat vstup od uživatele a
zapíše jej do textového souboru.
Modifikujte předchozí zadání tak, že na závěr vytvořený soubor opět
přečtete a vypíšete na obrazovku. V tomto případě musíme, před otevřením
souboru pro čtení, zapsaný soubor uzavřít.
-
V C++ je možno dědit při vytváření nové třídy metody a datové složky od
dvou nebo více tříd předků. Toto se označuje jako vícenásobná dědičnost.
Vícenásobná dědičnost je ukázána v následujícím programu.
class dodavka {
protected:
float naklad;
float vaha_objektu;
float spotreba;
public:
void inicializace(float
na, float va, float sp){
naklad = na;
vaha_objektu = va;
spotreba = sp;
}
float ucinnost(void){
return (naklad / (naklad + vaha_objektu));
}
float naklady_na_tunu(float
cena_paliva){
return (cena_paliva / (naklad / 2000.0));
}
};
class ridic {
protected:
float hod_mzda;
public:
void inicializace(float
mzda){hod_mzda = mzda; }
float naklady_na_km(void)
{return (hod_mzda / 90.0); }
};
class rizene_auto
: public dodavka, public ridic {
public:
void celk_inicializace(float
na, float va, float sp, float mzda){
naklad = na;
vaha_objektu = va;
spotreba = sp;
hod_mzda = mzda;
}
float naklady_na_cely_den(float
naklady_na_palivo){
return (8.0 * hod_mzda +
8.0 * naklady_na_palivo * 90.0 / spotreba);
}
};
int main(int argc,
char **argv)
{
rizene_auto
novak_ford;
novak_ford.celk_inicializace(5000.0,
3000.0, 8.3, 76.5);
cout <<
"Účinnost Fordu je " << novak_ford.ucinnost() << endl;
cout <<
"Náklady na km pro řidiče Nováka jsou " <<
novak_ford.naklady_na_km() << endl;
cout <<
"Náklady na řízení Fordu řidičem Novákem jsou na den " <<
novak_ford.naklady_na_cely_den(20.0) << endl;
return 0;
}
Pro zjednodušení hledání kódu v tomto programu jsou všechny metody
implementovány jako vnořené. Všechny složky tříd jsou deklarovány jako
chráněné a jsou tedy přístupné v odvozených třídách. Kód pro všechny třídy
je velmi jednoduchý, neboť se zaměřujeme hlavně na studium dědičnosti.
Na červeném řádku deklarujeme třídu rizene_auto, která dědí všechna
data a metody od obou dříve definovaných tříd. Při vícenásobné dědičnosti
zapisujeme za klíčové slovo public (případně jiné přístupové specifikátory)
jména všech tříd předků. V tomto případě nedefinujeme v odvozené třídě
žádné nové datové složky, ale přidáváme do ní dvě nové metody.
V hlavním programu deklarujeme objekt novak_ford, který popisuje
nějakého Nováka řídícího dodávku Ford. Tento objekt se skládá ze čtyř datových
složek z nichž tři pocházejí od třídy dodavka a jedna od třídy ridic.
Se všemi těmito čtyřmi složkami můžeme pracovat ve všech metodách třídy
rizene_auto
a to stejně jako při jednoduché dědičnosti. Všechna pravidla platící pro
jednoduchou dědičnost platí i pro vícenásobnou dědičnost. V hlavním programu
jsme nedeklarovali žádný objekt tříd předků. Použily bychom je normálním
způsobem. V našem programu je v obou třídách předků deklarována metoda
inicializace. Zjistěte, která z nich se použije, zašleme-li objektu novak_ford
zprávu inicializace.
-
V následující verzi programu jsou ve všech třídách metody naklady_na_cely_den.
Tento program ukazuje jak používat tyto stejnojmenné metody a jak určit,
kterou z nich chceme použít. Program si prostudujte.
class dodavka {
protected:
float naklad;
float vaha_objektu;
float spotreba;
public:
void inicializace(float
na, float va, float sp){
naklad = na;
vaha_objektu = va;
spotreba = sp;
}
float ucinnost(void){
return (naklad / (naklad + vaha_objektu));
}
float naklady_na_tunu(float
cena_paliva){
return (cena_paliva / (naklad / 2000.0));
}
float naklady_na_cely_den(float
naklady_na_palivo){
return (8.0 * naklady_na_palivo * 90.0 / spotreba);
}
};
class ridic {
protected:
float hod_mzda;
public:
void inicializace(float
mzda){hod_mzda = mzda; }
float naklady_na_km(void)
{return (hod_mzda / 90.0); }
float naklady_na_cely_den(float
prescas_mzda){
return (8.0 * hod_mzda);
}
};
class rizene_auto
: public dodavka, public ridic {
public:
void celk_inicializace(float
na, float va, float sp, float mzda){
naklad = na;
vaha_objektu = va;
spotreba = sp;
hod_mzda = mzda;
}
float naklady_na_cely_den(float
naklady_na_palivo){
return (8.0 * hod_mzda +
8.0 * naklady_na_palivo * 90.0 / spotreba);
}
};
int main(int argc,
char **argv)
{
rizene_auto
novak_ford;
novak_ford.celk_inicializace(5000.0,
3000.0, 8.3, 76.5);
cout <<
"Účinnost Fordu je " << novak_ford.ucinnost() << endl;
cout <<
"Náklady na km pro řidiče Nováka jsou " <<
novak_ford.naklady_na_km() << endl;
cout <<
"Náklady na Ford jsou na den " <<
novak_ford.dodavka::naklady_na_cely_den(20.0)
<<
endl;
cout <<
"Náklady na řidiče Nováka jsou na den " <<
novak_ford.ridic::naklady_na_cely_den(95.0)
<< endl;
cout <<
"Náklady na řízení Fordu řidičem Novákem jsou na den " <<
novak_ford.naklady_na_cely_den(20.0)
<< endl;
return 0;
}
-
V další ukázce se používá u obou tříd předků datová složka se jménem vaha_objektu.
Objekt třídy rizene_auto tedy dědí dvě složky stejného jména. Prostudujte
si v tomto programu, jak se takovéto složky používají.
class dodavka {
protected:
float naklad;
float vaha_objektu;
float spotreba;
public:
void inicializace(float
na, float va, float sp){
naklad = na;
vaha_objektu = va;
spotreba = sp;
}
float ucinnost(void){
return (naklad / (naklad + vaha_objektu));
}
float naklady_na_tunu(float
cena_paliva){
return (cena_paliva / (naklad / 2000.0));
}
};
class ridic {
protected:
float hod_mzda;
float vaha_objektu;
public:
void inicializace(float
mzda, float vaha){
hod_mzda = mzda;
vaha_objektu = vaha;
}
float naklady_na_km(void)
{return (hod_mzda / 90.0); }
float vaha_ridice(void)
{return (vaha_objektu);}
};
class rizene_auto
: public dodavka, public ridic {
public:
void celk_inicializace(float
na, float va, float sp, float mzda){
naklad = na;
dodavka::vaha_objektu = va;
spotreba = sp;
hod_mzda = mzda;
}
float naklady_na_cely_den(float
naklady_na_palivo){
return (8.0 * hod_mzda +
8.0 * naklady_na_palivo * 90.0 / spotreba);
}
float celkova_vaha(void){
return (dodavka::vaha_objektu + ridic::vaha_objektu);
}
};
int main(int argc,
char **argv)
{
rizene_auto
novak_ford;
novak_ford.celk_inicializace(5000.0,
3000.0, 8.3, 76.5);
novak_ford.ridic::inicializace(80.0,
85.0);
cout <<
"Učinnost Fordu je " << novak_ford.ucinnost() << endl;
cout <<
"Náklady na km pro řidiče Nováka jsou " <<
novak_ford.naklady_na_km() << endl;
cout <<
"Náklady na řízení Fordu řidičem Novákem jsou na den " <<
novak_ford.naklady_na_cely_den(20.0) << endl;
cout <<
"Celková váha je " << novak_ford.celkova_vaha() << endl;
return 0;
}
Protože Object Pascal nezná vícenásobnou dědičnost, nelze používat
vícenásobnou dědičnost ve třídách odvozených od tříd VCL (tříd knihovny
vizuálních komponent; knihovna je naprogramovaná v Object Pascalu). Vícenásobnou
dědičnost lze tedy používat pouze mimo rámec VCL.