Struktury

Stronę tą wyświetlono już: 238 razy

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:

Listing 1
  1. struct student // deklaracja struktury typu student
  2. {
  3. char Nazwisko[100]; // pole struktury będące 100 elementową tablicą statyczną typu char
  4. char Imie[100]; // pole struktury będące 100 elementową tablicą statyczną typu char
  5. float MatematykaOcena; // pole struktury typu float
  6. float FizykaOcena; // pole struktury typu float
  7. }; // 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:

Listing 2
  1. 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
  2. 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:

Listing 3
  1. student tstudentow[] = {{"Kowalski","Leon",5,5},{"Pietrucha","Mirosław",3,3}}; // inicjowanie statycznej tablicy dwuelementowej wraz z jednoczesnym ustawieniem danych zawartych w polach
  2. for(int i = 0; i < sizeof(tstudentow) / sizeof(student) /* określenie rozmiaru tablicy (poprawne tylko dla statycznie utworzonych tablic)*/; i++){
  3. 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
  4. }

Dynamiczne deklarowanie egzemplarza struktury typu student:

Listing 4
  1. student *lpstudent = new student;
  2. 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
  3. strcpy(lpstudent->Imie,"Leon");
  4. lpstudent->MatematykaOcena = 5; // inicjalizowanie pola wskaźnika na strukturę
  5. lpstudent->FizykaOcena = 5;
  6. 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
  7. if(lpstudent){ // jeżeli lpstudent ma przypisany jakiś adres w pamięci to
  8. delete lpstudent; // zwolnij tą pamięć
  9. lpstudent = 0; // i przypisz zerowy adres aby dać znać, że wskaźnik już na nic nie wskazuje
  10. }

Skoro mamy studenta, to wypadałoby utworzyć strukturę typu grupa opisującą daną grupę studentów danego roku studiów:

Listing 5
  1. struct grupa
  2. {
  3. char kierunekStudiow[100];
  4. student *tstudent; // wewnątrz struktury jest wskaźnik do struktury typu student (będzie to tablica dynamiczna)
  5. unsigned short int nrOfStudents; // to pole będzie przechowywało informację o liczbie studentów w grupie
  6. };

Inicjalizacja podstawowa egzemplarza powyższego typu struktury będzie wyglądała następująco:

Listing 6
  1. grupa gr={"Informatyka",0,0};

Do dodawania nowych studentów stwożyć należy sobie taką oto funkcję:

Listing 7
  1. void DodajStudentaDoGrupy(grupa &gr, const char* nazwisko, const char* imie, float matematykaOcena, float fizykaOcena){
  2. if(gr.nrOfStudents > 29) // jeżeli studentów jest już trzydziestu to
  3. return ; // nie pozwalaj na dodawanie kolejnych studentów do grupy
  4. student *tstudent = new student[gr.nrOfStudents + 1]; // przydzielanie pamięci o 1 większej niż obecna pamięć zawierająca listę studenciaków
  5. for(unsigned int i = 0; i < gr.nrOfStudents; i++){ // dla wszystkich elementów tablicy dynamicznej gr.
  6. tstudent[i] = gr.tstudent[i]; // kopiuj zawartość danego elementu tablicy gr.tstudent do tablicy tstudent
  7. }
  8. if(gr.nrOfStudents){ // gdy liczba studentów nie jest równa 0
  9. delete[] gr.tstudent; // zwalniaj pamięć
  10. }
  11. gr.tstudent = tstudent; // przypisanie wskaźnika tstudent do gr.tstudent
  12. strcpy(tstudent[gr.nrOfStudents].Nazwisko, nazwisko); // kopiowanie nazwiska dodawanego studenciaka
  13. strcpy(tstudent[gr.nrOfStudents].Imie, imie); // kopiowanie imienia dodawanego studenciaka
  14. tstudent[gr.nrOfStudents].MatematykaOcena = matematykaOcena; // przypisanie oceny z matmy
  15. tstudent[gr.nrOfStudents].FizykaOcena = fizykaOcena; // przypisanie oceny z fizy
  16. gr.nrOfStudents ++; // zwiększenie zmiennej pamiętającej liczbę studenciaków
  17. }

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:

Listing 8
  1. void WypiszDaneStudenta(student &s){ // funkcja wypisuje dane danego studenciaka
  2. cout<<"Nazwisko: "<<s.Nazwisko<<" Imie: "<<s.Imie<<endl<<"Ocena koncowa z matematyki: "<<s.MatematykaOcena<<"Ocena koncowa z fizyki: "<<s.FizykaOcena;
  3. }
  4. void WypiszDaneGrupy(grupa &gr){ // funkcja wypisuje dane na temat grupy, oraz wypisuje nazwiska i imienia wszystkich stodenciaków
  5. cout<<"Kierunek: "<<gr.kierunekStudiow<<endl<<endl<<"Liczba studentow: "<< gr.nrOfStudents << endl << endl <<"Lista studentow: "<<endl<<endl;
  6. for(unsigned int i = 0; i < gr.nrOfStudents; i++){
  7. cout<<"Nazwisko: "<<gr.tstudent[i].Nazwisko<<" Imie: "<<gr.tstudent[i].Imie<<endl;
  8. }
  9. }

Potrzebna będzie jeszcze funkcja, która przed zamknięciem programu będzie zwalniała przydzieloną dynamicznie pamięć:

Listing 9
  1. void UsunWszystkichStudentow(grupa &gr){ // funkcja zwalnia pamięć
  2. if(gr.nrOfStudents){
  3. delete[] gr.tstudent; // zwalnianie przypisanej pamięci
  4. gr.tstudents = 0; // przypisanie zerowego adresu
  5. gr.nrOfStudents = 0; // zero studenciaków
  6. }
  7. }

Teraz kod programu umieszczony w głównej funkcji main:

Listing 10
  1. int main(){
  2. grupa gr={"Informatyka",0,0}; // tworzenie grupy pozbawionej studentów
  3. DodajStudentaDoGrupy(gr,"Kowalski","Leon",5,5); // dodawanie pierwszego studenta
  4. DodajStudentaDoGrupy(gr,"Pietrucha","Mirosław",3,3); // dodawanie drugiego studenta
  5. WypiszDaneGrupy(gr); // wypisywanie informacji o grupie i studentach
  6. UsunWszystkichStudentow(gr); // zwalnianie pamięci przed wyjściem z programu
  7. cout<<"Wcisnij enter, aby zamknac program...";
  8. cin.get();
  9. return 0;
  10. }

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.

Komentarze