-
Struktura je typ, který reprezentuje uživatelsky definovanou množinu pojmenovaných
složek (prvků). Tyto složky mohou být libovolného typu a mohou mít libovolné
pořadí. Struktury se deklarují pomocí klíčového slova struct, např.
struct TStruktura
{
// TStruktura je označení struktury
deklarace složek struktury
};
...
struct TStruktura
s, *ps, poleS[10] // s je struktura TStruktura,
ps je ukazatel na strukturu TStruktura
// poleS je pole prvků typu TStruktura
Pokud vynecháme jméno (označení) struktury, dostaneme tzv. nepojmenovanou
strukturu. Nepojmenované struktury se mohou používat při deklaraci proměnných
daného strukturového typu, ale nikde jinde již další objekty tohoto typu
definovat nemůžeme:
struct { ... } s,
*ps, poleS[10];
Při deklarování struktur je možné pomocí typedef (stejně jako
pro jiné typy - poslední identifikátor na řádku je nový název pro typ popsaný
mezi typedef a tímto identifikátorem) vytvořit nový specifikátor
datového typu:
typedef struct TStruktura
{ ... } STRUKT;
STRUKT s, *ps, poleS[10];
// totéž jako původní deklarace
Většinou není zapotřebí pojmenování struktury a typedef současně.
Můžeme je ale použít. Je také možné vytvořit specifikátor nepojmenované
struktury.
Seznam deklarací složek uzavřený ve složených závorkách deklaruje typy
a jména složek struktury.
Funkce může vracet strukturu nebo ukazatel na strukturu. Struktura
může být předána jako parametr funkce přímo, jako ukazatel nebo odkaz na
strukturu.
Přístup ke složkám struktur se provádí prostřednictvím operátorů .a
->.
Operátor .se nazývá přímý selektor
složky, operátor -> se nazývá nepřímý
selektor složky (výraz ukazs->m
je synonymum pro (*ukazs).m
-
používá se, když máme ukazatel na strukturu).
struct TStructura
{
int i;
char str[2];
double d;
} s, *ukazs = &s;
...
s.i = 3;
// přiřazení složce i struktury s
ukazs->d = 1.23;
// přiřazení složce d struktury s (struktura je identifikovaná ukazatelem
ukazs)
Pokud jednou ze složek struktury B je struktura A, pak
složky struktury A mohou být zpřístupněny dvojí aplikací selekčních
operátorů. Struktury je možno přiřazovat jen v tom případě, že jsou stejného
typu.
Ukazatel na strukturu typu A se může objevit v deklaraci jiné
struktury B ještě předtím, než je A deklarováno. Např.
struct A;
// předběžná deklarace
struct B { struct
A *pa; };
struct A { struct
B *pb; };
První deklarace A se nazývá předběžná, protože A prozatím
není celá definována. Takováto předběžná deklarace stačí k tomu, aby jsme
mohli použít v definici B ukazatel na A, neboť B nepotřebuje
znát velikost A.
-
Předpokládejme, že si chceme udržovat adresář svých známých. Vytvoříme
tedy strukturu se složkami, které se obvykle používají v adresáři. Např.
struct adresar {
char jmeno[20];
char prijmeni[20];
char ulice[50];
char mesto[20];
int psc;
bool pritel;
};
Struktura má 4 znakové, jednu celočíselnou a jednu logickou složku.
Po deklaraci struktury ji můžeme použít. Nejprve je nutno vytvořit instanci
struktury (proměnnou pro uložení struktury). To provedeme takto:
adresar zaznam;
Tento příkaz alokuje paměť pro strukturu a přiřadí alokovanou paměť
proměnné zaznam. Nyní, když máme instanci struktury, můžeme přiřadit
hodnoty jejím složkám:
strcpy(zaznam.jmeno,
"Karel");
strcpy(zaznam.prijmeni,
"Novak");
strcpy(zaznam.ulice,
"Palackeho 876");
strcpy(zaznam.mesto,
"Pardubice"
zaznam.psc = 53002;
zaznam.pritel = false;
Toto lze provést také přímo při deklaraci struktury:
adresar zaznam =
{ "Karel", "Novak", "Palackeho 876",
"Pardubice", 53002, false};
Stejně jako můžeme mít pole celých čísel nebo pole znaků, můžeme mít
i pole struktur. Např.
adresar seznam[5];
strcpy(seznam[0].jmeno,
"Karel");
seznam[4].pritel
= false;
// atd.
Zde používáme operátor indexace a operátor selektoru složky.
-
Zdrojový soubor je textový soubor obsahující zdrojový kód programu. Překladač
přebírá soubory zdrojového kódu, zpracovává je a vytváří strojový kód,
který může být spuštěn na počítači.
Při našem seznamování s programováním, řešíme samé jednoduché úlohy.
Při vytváření reálných aplikací se bude jednat o podstatně složitější úlohy.
Normální program se obvykle skládá z několika zdrojových souborů. Programový
kód je rozdělen do několika různých zdrojových souborů z několika důvodů.
Základním důvodem je udržování kódu týkajícího se jednoho problému v jednom
souboru, což umožňuje v případě potřeby snadné nalezení jisté části kódu.
Při překladu, je nejprve přeložen každý zdrojový soubor (CPP) do objektového
souboru (OBJ) a potom každý přeložený modul je sestaven sestavovacím programem
do jednoho spustitelného souboru (EXE). Sestavovací program také připojí
další potřebné soubory, jako jsou soubory zdrojů (RES) a knihovní soubory
(LIB).
Deklarace pro třídy a struktury jsou drženy v oddělených souborech
nazvaných hlavičkové soubory. Tyto soubory mají příponu H nebo HPP. Hlavičkové
soubory obsahují pouze deklarace tříd, struktur a funkcí. Není vhodné vkládat
do nich kód příkazů. Z tohoto pravidla je jedna výjimka. Do hlavičkových
souborů je možno vkládat vložené (inline) funkce. S tím se ale seznámíme
později.
Jestliže potřebujeme vytvořit hlavičkový soubor, pak zvolíme File
| New a na stránce New dvojitě klikneme na ikonu Text.
C++ Builder vytvoří nový textový soubor a zobrazí jej v Editoru kódu. Zadáme
kód našeho hlavičkového souboru a uložíme vytvořený soubor s příponou H.
Po vytvoření hlavičkového souboru pro třídu nebo strukturu, můžeme
direktivou include vložit tento hlavičkový soubor do libovolného
zdrojového souboru, ve kterém deklaraci třídy nebo struktury potřebujeme.
Hlavičkové soubory obvykle vytváříme tak, aby bylo zajištěno, že do
programu budou vloženy pouze jednou. Používáme k tomu direktivy podmíněného
překladu (viz zadání 1 v kapitole 10).
Hlavičkové soubory mohou obsahovat více než jednu deklaraci třídy nebo
struktury. Použití samostatného hlavičkového souboru pro každou třídu nebo
strukturu ale pomáhá udržovat organizaci projemtu a usnadňuje opětovné
použití tříd a struktur v dalších programech. Někdy je možno vložit skupinu
svázaných tříd do jednoho hlavičkového souboru.
-
Nyní se pokusíme vytvořit konzolovou aplikaci, ve které uživatel zadá tři
jména a adresy a uloží tyto záznamy do pole struktur. Po zadání těchto
informací, jsou informace zobrazeny na obrazovce. Dále je možno zadat číslo
záznamu, který chceme opětovně zobrazit. V této aplikaci definici struktury
uvedeme v samostatném hlavičkovém souboru. Nejprve následuje výpis vlastního
programu.
#include <iostream.h>
#include <conio.h>
#include <stdlib.h>
#pragma hdrstop
#include "structur.h"
void zobrazZaznam(int,
adresar adrZaz);
int main(int argc,
char **argv)
{
adresar seznam[3];
cout <<
endl;
int index
= 0;
do {
cout << "Jméno: ";
cin.getline(seznam[index].jmeno, sizeof(seznam[index].jmeno)-1);
cout << "Příjmení: ";
cin.getline(seznam[index].prijmeni,
sizeof(seznam[index].prijmeni)-1);
cout << "Ulice: ";
cin.getline(seznam[index].ulice, sizeof(seznam[index].ulice)-1);
cout << "Město: ";
cin.getline(seznam[index].mesto, sizeof(seznam[index].mesto)-1);
cout << "Psč: ";
char buff[10];
cin.getline(buff, sizeof(buff)-1);
seznam[index].psc = atoi(buff);
index++;
cout << endl;
} while (index
< 3);
clrscr();
for(int i
= 0; i < 3; i++) {
zobrazZaznam(i, seznam[i]);
}
cout <<
"Zadej číslo záznamu: ";
int zaz;
do {
zaz = getch();
zaz -= 49;
} while (zaz
< 0 || zaz > 2);
adresar pom
= seznam[zaz];
clrscr();
zobrazZaznam(zaz,
pom);
getch();
return 0;
}
void zobrazZaznam(int
cis, adresar adrZaz)
{
cout <<
"Záznam " << (cis + 1) << ":" << endl;
cout <<
"Jméno: " << adrZaz.jmeno << " " << adrZaz.prijmeni <<
endl;
cout <<
"Adresa: " << adrZaz.ulice << endl;
cout <<
" " << adrZaz.mesto <<
endl;
cout <<
" " << adrZaz.psc <<
endl << endl;
}
a hlavičkový soubor STRUCTUR.H je tento (uložíme jej do stejného adresáře
jako zdrojový soubor):
#ifndef _STRUCTUR_H
#define _STRUCTUR_H
struct adresar {
char jmeno[20];
char prijmeni[20];
char ulice[50];
char mesto[20];
int psc;
};
#endif
V programu vidíme několik nám zatím neznámých věcí. Program používá
funkci getline třídy cin k získání vstupu od uživatele. Je
to z důvodu nepříjemného chování operátoru >> při čtení textů, které mohou
obsahovat mezery. Druhý parametr getline je použit k omezení délky
vstupujícího řetězce (zajistíme tím nepřepsání informací za koncem složky).
Operátor sizeof je použit k určení velikosti složky a tedy k určení,
kolik znaků je do ní možno bezpečně uložit.
Funkce atoi je také nová (je použita při čtení poštovního směrovacího
čísla). Tato funkce přebírá znakový řetězec a převádí jej na celočíselnou
hodnotu. Zadaný text pro složku psc je nutno převést na celé číslo.
Funkce zobrazZaznam má dva parametry. První parametr udává číslo
zobrazovaného záznamu. Záznamy jsou číslovány od nuly a jelikož ve výpisu
dáváme přednost jejich číslování od 1, přičítáme k tomuto číslu před výpisem
jedničku. Druhý parametr funkce zobrazZáznam je instance struktury
adresar.
Je použita lokální instance této struktury a uvnitř funkce je její obsah
zobrazen.
V tomto případě předáváme strukturu hodnotou. Tzn. během volání funkce
je vytvořena kopie struktury. Toto není moc efektivní, protože vytvoření
kopie nějakou dobu trvá a spotřebovává místo v paměti. Je výhodnější předávat
strukturu odkazem, ale to zatím neumíme.
Funkci zobrazZaznam voláme na dvou místech v našem programu.
Na těchto místech by mohl být uveden obsah těla funkce a program by pracoval
stejně. Vložením tohoto kódu do funkce se snažíme zabránit opakovanému
výskytu stejného kódu. Pokud se v našem programu opakovaně vyskytuje stejný
kód, je vhodné z něj udělat funkci, kterou pak voláme, když tento kód je
zapotřebí provést.
Podívejme se ještě na následující kód:
do {
zaz = getch();
zaz -= 49;
} while (zaz <
0 || zaz > 2);
Tento kód čte znak z klávesnice pomocí funkce getch (zatím jsme
ji používali k čekání na konci programu). Tato funkce vrací kód stisknuté
klávesy. Protože ASCII hodnota klávesy 1 je 49, klávesy 2 je 50, atd.,
získáme odečtením 49 od kódu přečteného znaku číselnou hodnotu klávesy
zmenšenou o 1 (např. při stisknuté klávese 1 dostaneme hodnotu 0). Tento
cyklus tedy probíhá tak dlouho, dokud nestiskneme některou z kláves 1,
2 nebo 3.
Podívejme se ještě na řádek
adresar pom = seznam[zaz];
Tento kód vytváří instanci struktury adresar a přiřadí ji obsah
jedné struktury z pole struktur. Je zde možno použít operátor přiřazení,
protože překladač ví, jak kopírovat jednu strukturu do jiné. Můžeme tedy
snadno vytvořit kopii celé struktury.
-
Pokuste se upravit předchozí program tak, aby bylo možno zadávat více záznamů
(až 9).
-
V předchozí konzolové aplikaci jsme přečetli údaje o několika osobách.
Tyto údaje je možno vložit do binárního souboru. Musíme deklarovat:
FILE *soubor;
a před ukončení programu vložíme příkazy (předpokládáme, že aktuální
počet záznamů je uložen v proměnné n):
if ((soubor = fopen("KART.DTA",
"wb")) == NULL) {
cout <<
"Nelze vytvořit soubor" << endl;
return 1;
}
for (i = 0; i <
n; i++)
fwrite(&seznam[i],
sizeof(adresar), 1, soubor);
fclose(soubor);
Proveďte tyto změny a program vyzkoušejte. Funkce fwrite zapisuje
počet vět určený třetím parametrem do souboru určeného čtvrtým parametrem.
Délku věty určuje druhý parametr a počátek první věty určuje první parametr.
-
Soubor vytvořený v předchozím zadání je možno opět načíst a používat v
jiném programu. Následující program ukazuje, jak informace ze souboru uložit
do pole v paměti a vypsat je na obrazovce.
#include "struktur.h"
FILE *soubor;
int n, i;
adresar veta[10];
if ((soubor = fopen("KART.DTA",
"rb")) == NULL) {
cout <<
"Nelze otevřít soubor" << endl;
return 1;
}
i = 0;
while (fread(&veta[i],
sizeof(adresar), 1, soubor) > 0)
i++;
n = i;
fclose(soubor);
for (i = 0; i <
n; i++)
zobrazZaznam(i,
veta[i]);
Vyzkoušejte. Vložením hlavičkového souboru STRUCTUR.H zajistíme, že
program bude pracovat se stejnou strukturou záznamu.
-
Přejdeme opět k aplikacím GUI. Nejprve se začneme zabývat formulářem. Mimo
vlastností, se kterými jsme se již seznámili, má formulář další důležité
vlastnosti. Vlastnost BorderStyle určuje styl
okraje formuláře (např. hodnota bsNone určuje
formulář bez okrajů, tzn. nelze měnit jeho velikost). Měňte tuto vlastnost
a vyzkoušejte, jak se změna projeví na vzhledu a chování formuláře za běhu
aplikace.
-
Formulář má dále vlastnost BorderIcons (ikony
rámu formuláře, tj. minimalizační a maximalizační tlačítka apod.). Před
jménem této vlastnosti je v Inspektoru objektů uveden znak + (indikace,
že vlastnost se skládá z podvlastností). Klikneme-li dvojitě na takto označeném
jméně vlastnosti, jsou na následujících řádcích zobrazeny podvlastnosti
a znak + se změní na - (opětovným dvojitým kliknutím se vrátíme k původnímu
zobrazení). Při tomto ?rozbalení? vlastnosti, můžeme jednotlivé podvlastnosti
nastavovat samostatně. Pokuste se z formuláře odstranit maximalizační tlačítko
(BorderStyle musí být nastaveno na bsSizeable;
změna se projeví až při spuštění aplikace).
-
Vlastnosti Position a WindowState
určují způsob zobrazení formuláře na obrazovce. Position
určuje, zda formulář získá pozici a velikost danou při návrhu, nebo zda
se použije velikost a pozice navržená Windows za běhu aplikace. WindowState
určuje počáteční stav formuláře (minimalizovaný, maximalizovaný nebo normální).
Pokuste se změnit některou z těchto vlastností a vyzkoušejte jak se změna
projeví.
-
Vlastnost ShowHint komponenty určuje, zda
komponenta zobrazí nápovědu, když se na ní na okamžik zastavíme kurzorem
myši. Zobrazený text je určen vlastností Hint.
Umístěte na formulář nějakou komponentu a zajistěte pro ní zobrazování
nápovědy.
-
Komponentu Panel lze používat pro ukládání
dalších komponent. Můžeme z ní vytvořit např. stavový řádek nebo na ní
vložit komponenty, které tvoří nějaký logický celek. Vlastnost Align
určuje umístění komponenty. Možné hodnoty této vlastnosti mají následující
význam: alTop (panel je umístěn na horní okraj
formuláře, zabírá celou šířku formuláře a sleduje i změnu šířky formuláře;
obdobně platí i pro další hodnoty této vlastnosti), alRight
(panel je umístěn na pravý okraj formuláře), alBottom
(spodní okraj), alLeft (levý okraj), alClient
(celá plocha formuláře) a alNone (nemění se;
zůstává tak jak nastavil uživatel). Umístěte na formulář komponentu panelu
a vyzkoušejte vliv hodnot vlastnosti Align
na umístění komponenty.
-
U komponenty Panel lze pomocí vlastností BevelInner,
BevelOuter,
BevelWidth,
BorderStyle
a BorderWidth měnit způsob orámování panelu.
Nastavte hodnotu vlastnosti BevelWidth na
10 (pro lepší viditelnost) a vyzkoušejte vliv změn ostatních těchto vlastností
na zobrazení okraje.