Struktury umożliwiają przechowywanie pod jedną nazwą zmiennej złożonej wielu typów prostych a nawet i egzemplarzy struktur czy też klas, dzięki czemu dużo łatwiej jest grupować takie zmienne w tablicach. Poniżej zamieszczam prosty przykład struktury typu student:
struct student // deklaracja struktury typu student
{
char Nazwisko[100]; // pole struktury będące 100 elementową tablicą statyczną typu char
char Imie[100]; // pole struktury będące 100 elementową tablicą statyczną typu char
float MatematykaOcena; // pole struktury typu float
float FizykaOcena; // pole struktury typu float
}; // ważne, aby nie zapominać o średniku po zamknięciu nawiasów klamrowych
Jak widać, udało się w jednej definicji struktury typu student zamieścić kilka typów prostych oraz typy łańcuchowe, teraz utworzyć należy egzemplarz struktury student w sposób następujący:
student s = {"Kowalski","Leon",5,5}; // tak tworzy się i jednocześnie inicjalizuje się zmienną o nazwie s będącą egzemplarzem klasy typu student
cout<<"Nazwisko: "<<s.Nazwisko /*odwołanie się do pola struktury o nazwie Nazwisko */<<" Imie: "<<s.Imie<<"\nOcena koncowa z matematyki: "<<s.MatematykaOcena<<"\nOcena końcowa z fizyki: "<<s.FizykaOcena; // wypisywanie zawartości pól struktury
Powyżej pokazany został przykład odwołania się do elementów struktury typu student za pomocą operatora . (kropki). W podobny sposób deklaruje się tablice statyczne struktur:
student tstudentow[] = {{"Kowalski","Leon",5,5},{"Pietrucha","Mirosław",3,3}}; // inicjowanie statycznej tablicy dwuelementowej wraz z jednoczesnym ustawieniem danych zawartych w polach
for(int i = 0; i < sizeof(tstudentow) / sizeof(student) /* określenie rozmiaru tablicy (poprawne tylko dla statycznie utworzonych tablic)*/; i++){
cout<<"Nazwisko: "<<tstudentow[i].Nazwisko /*odwołanie się do pola struktury o nazwie Nazwisko i-tego elementu tablicy*/<<" Imie: "<<tstudentow[i].Imie<<"\nOcena koncowa z matematyki: "<<s.MatematykaOcena<<"\nOcena koncowa z fizyki: "<<tstudentow[i].FizykaOcena; // wypisywanie zawartości pól struktury
}
Dynamiczne deklarowanie egzemplarza struktury typu student:
student *lpstudent = new student;
strcpy(lpstudent->Nazwisko,"Kowalski"); // kopiowanie ciągu znaków "Kowalski" do pola Nazwisko za pomocą funkcji strcpy. Aby możliwe było korzystanie z tej funkcji trzeba załączyć do projektu plik nagłówkowy string.h
strcpy(lpstudent->Imie,"Leon");
lpstudent->MatematykaOcena = 5; // inicjalizowanie pola wskaźnika na strukturę
lpstudent->FizykaOcena = 5;
cout<<"Nazwisko: "<<lpstudent->Nazwisko /*odwołanie się do pola wskaźnika o nazwie Nazwisko struktury typu student*/<<" Imie: "<<lpstudent->Imie<<"\nOcena koncowa z matematyki: "<<lpstudent->MatematykaOcena<<"\nOcena końcowa z fizyki: "<<lpstudent->FizykaOcena; // wypisywanie zawartości pól struktury
if(lpstudent){ // jeżeli lpstudent ma przypisany jakiś adres w pamięci to
delete lpstudent; // zwolnij tą pamięć
lpstudent = 0; // i przypisz zerowy adres aby dać znać, że wskaźnik już na nic nie wskazuje
}
Skoro mamy studenta, to wypadałoby utworzyć strukturę typu grupa opisującą daną grupę studentów danego roku studiów:
struct grupa
{
char kierunekStudiow[100];
student *tstudent; // wewnątrz struktury jest wskaźnik do struktury typu student (będzie to tablica dynamiczna)
unsigned short int nrOfStudents; // to pole będzie przechowywało informację o liczbie studentów w grupie
};
Inicjalizacja podstawowa egzemplarza powyższego typu struktury będzie wyglądała następująco:
grupa gr={"Informatyka",0,0};
Do dodawania nowych studentów stwożyć należy sobie taką oto funkcję:
void DodajStudentaDoGrupy(grupa &gr, const char* nazwisko, const char* imie, float matematykaOcena, float fizykaOcena){
if(gr.nrOfStudents > 29) // jeżeli studentów jest już trzydziestu to
return ; // nie pozwalaj na dodawanie kolejnych studentów do grupy
student *tstudent = new student[gr.nrOfStudents + 1]; // przydzielanie pamięci o 1 większej niż obecna pamięć zawierająca listę studenciaków
for(unsigned int i = 0; i < gr.nrOfStudents; i++){ // dla wszystkich elementów tablicy dynamicznej gr.
tstudent[i] = gr.tstudent[i]; // kopiuj zawartość danego elementu tablicy gr.tstudent do tablicy tstudent
}
if(gr.nrOfStudents){ // gdy liczba studentów nie jest równa 0
delete[] gr.tstudent; // zwalniaj pamięć
}
gr.tstudent = tstudent; // przypisanie wskaźnika tstudent do gr.tstudent
strcpy(tstudent[gr.nrOfStudents].Nazwisko, nazwisko); // kopiowanie nazwiska dodawanego studenciaka
strcpy(tstudent[gr.nrOfStudents].Imie, imie); // kopiowanie imienia dodawanego studenciaka
tstudent[gr.nrOfStudents].MatematykaOcena = matematykaOcena; // przypisanie oceny z matmy
tstudent[gr.nrOfStudents].FizykaOcena = fizykaOcena; // przypisanie oceny z fizy
gr.nrOfStudents ++; // zwiększenie zmiennej pamiętającej liczbę studenciaków
}
W powyższym kodzie pokazane zostało, jak przekazywać odwołania do struktur w funkcjach poprzez referencje. Dodam jeszcze dwie funkcje, które będą pomocne przy wczytywaniu nowych elementów tablicy do tablicy dynamicznej:
void WypiszDaneStudenta(student &s){ // funkcja wypisuje dane danego studenciaka
cout<<"Nazwisko: "<<s.Nazwisko<<" Imie: "<<s.Imie<<endl<<"Ocena koncowa z matematyki: "<<s.MatematykaOcena<<"Ocena koncowa z fizyki: "<<s.FizykaOcena;
}
void WypiszDaneGrupy(grupa &gr){ // funkcja wypisuje dane na temat grupy, oraz wypisuje nazwiska i imienia wszystkich stodenciaków
cout<<"Kierunek: "<<gr.kierunekStudiow<<endl<<endl<<"Liczba studentow: "<< gr.nrOfStudents << endl << endl <<"Lista studentow: "<<endl<<endl;
for(unsigned int i = 0; i < gr.nrOfStudents; i++){
cout<<"Nazwisko: "<<gr.tstudent[i].Nazwisko<<" Imie: "<<gr.tstudent[i].Imie<<endl;
}
}
Potrzebna będzie jeszcze funkcja, która przed zamknięciem programu będzie zwalniała przydzieloną dynamicznie pamięć:
Teraz kod programu umieszczony w głównej funkcji main:
int main(){
grupa gr={"Informatyka",0,0}; // tworzenie grupy pozbawionej studentów
DodajStudentaDoGrupy(gr,"Kowalski","Leon",5,5); // dodawanie pierwszego studenta
DodajStudentaDoGrupy(gr,"Pietrucha","Mirosław",3,3); // dodawanie drugiego studenta
WypiszDaneGrupy(gr); // wypisywanie informacji o grupie i studentach
UsunWszystkichStudentow(gr); // zwalnianie pamięci przed wyjściem z programu
cout<<"Wcisnij enter, aby zamknac program...";
cin.get();
return 0;
}
Powyższy przykład z użyciem tablic dynamicznych nie jest najlepszym rozwiązaniem, dlatego że konieczne jest tutaj przy każdorazowym dodawaniu nowych wartości deklarowanie nowej tablicy dynamicznej, kopiowanie elementów starej do nowej, zwalnianie pamięci, przepisywanie wskaźnika bla, bla, bla ... dużo pracy i łatwo się pomylić a w dodatku komputer też mocno obciążony zostaje zwłaszcza, gdy w grę wchodzą duże ilości danych do przekopiowania. O tym jak rozwiązać ten palący problem postaram się opowiedzieć podczas omawiania tematu dotyczącego tworzenia własnej listy dwukierunkowej.