Struktury

Z RNO-Wiki
(Różnice między wersjami)
(Metody)
Linia 3: Linia 3:
 
Jak już wiemy, w języku C/C++ zdefiniowanych jest wiele typów danych, np. <code>int</code>, <code>long long</code>, <code>char</code>, <code>string</code>, <code>void</code>, <code>float</code>, <code>double</code>.
 
Jak już wiemy, w języku C/C++ zdefiniowanych jest wiele typów danych, np. <code>int</code>, <code>long long</code>, <code>char</code>, <code>string</code>, <code>void</code>, <code>float</code>, <code>double</code>.
 
Jeśli jest to dla nas zbyt mało, to możemy zdefiniować swój własny typ złożony. Nazywamy go strukturą. Na przykład typ będący parą dwóch zmiennych <code>int</code>-a i <code>string</code>-a, w języku C++ definiujemy następująco:
 
Jeśli jest to dla nas zbyt mało, to możemy zdefiniować swój własny typ złożony. Nazywamy go strukturą. Na przykład typ będący parą dwóch zmiennych <code>int</code>-a i <code>string</code>-a, w języku C++ definiujemy następująco:
 
+
<source lang="cpp">
   '''struct''' para
+
   struct para
 
   {
 
   {
 
     int n;
 
     int n;
 
     string s;
 
     string s;
 
   };
 
   };
 
+
</source>
 
Teraz możemy deklarować zmienne typu <code>para</code> (nazwę można było wpisać prawie dowolną) w następujący sposób:
 
Teraz możemy deklarować zmienne typu <code>para</code> (nazwę można było wpisać prawie dowolną) w następujący sposób:
 
   para X;
 
   para X;
Linia 16: Linia 16:
 
== Pola struktury ==
 
== Pola struktury ==
 
Do '''pól struktury''' możemy dostać się po ''kropce'', mianowicie (kontynuujemy poprzedni przykład)
 
Do '''pól struktury''' możemy dostać się po ''kropce'', mianowicie (kontynuujemy poprzedni przykład)
 +
<source lang="cpp">
 
   int a;
 
   int a;
 
   a = X.n;
 
   a = X.n;
 
   cin >> X.s; // wczytywanie string-a ze standardowego wejścia
 
   cin >> X.s; // wczytywanie string-a ze standardowego wejścia
 +
</source>
  
 
Możemy także napisać własną funkcję, która zwraca zmienną typu <code>para</code>, np.
 
Możemy także napisać własną funkcję, która zwraca zmienną typu <code>para</code>, np.
 +
<source lang="cpp">
 
   para funkcja(string argument)
 
   para funkcja(string argument)
 
   {
 
   {
Linia 28: Linia 31:
 
     return wynik;    // ważne jest, że wynik jest typu 'para'
 
     return wynik;    // ważne jest, że wynik jest typu 'para'
 
   }
 
   }
 
+
</source>
 
== Metody ==
 
== Metody ==
 
Struktura może mieć nie tylko pola (zmienne pewnego typu); można także dla struktry zdefiniować tzw. '''metody'', czyli funkcje obsługujące interfejs struktury. Na przykład  
 
Struktura może mieć nie tylko pola (zmienne pewnego typu); można także dla struktry zdefiniować tzw. '''metody'', czyli funkcje obsługujące interfejs struktury. Na przykład  
<pre>
+
<source lang="cpp">
 
   struct punkt
 
   struct punkt
 
   {
 
   {
Linia 53: Linia 56:
 
     }
 
     }
 
   }; // koniec definicji struktury musi zawierać średnik
 
   }; // koniec definicji struktury musi zawierać średnik
</pre>
+
</source>
 
Teraz możemy poszaleć:
 
Teraz możemy poszaleć:
<pre>
+
<source lang="cpp">
 
   punkt tablica[100]; // deklarujemy tablice struktur punkt
 
   punkt tablica[100]; // deklarujemy tablice struktur punkt
 
   for (int i=0; i<100; i++) // wczytujemy 100 punktow
 
   for (int i=0; i<100; i++) // wczytujemy 100 punktow
Linia 71: Linia 74:
 
   cout << " W cwiartce nr 3 jest " << ile[3] << " punktow." << endl;
 
   cout << " W cwiartce nr 3 jest " << ile[3] << " punktow." << endl;
 
   cout << " W cwiartce nr 4 jest " << ile[4] << " punktow." << endl;
 
   cout << " W cwiartce nr 4 jest " << ile[4] << " punktow." << endl;
</pre>
+
</source>
  
 
Po co to? Otóz metody są bardzo przydatne. Mogą coś dla nas zrobić tak jak funkcje. Należy rozumieć, że tutaj każda zmienna (struktura) ma swoją własną funkcję. Jeśli w definicji metody używamy zmiennych o nazwach takich, jak jej pola to wówczas odwołujemy się do pól struktury. Metody mogą więc nieźle '''namieszać''' w strukturze.
 
Po co to? Otóz metody są bardzo przydatne. Mogą coś dla nas zrobić tak jak funkcje. Należy rozumieć, że tutaj każda zmienna (struktura) ma swoją własną funkcję. Jeśli w definicji metody używamy zmiennych o nazwach takich, jak jej pola to wówczas odwołujemy się do pól struktury. Metody mogą więc nieźle '''namieszać''' w strukturze.
 
Kolejny przykład:
 
Kolejny przykład:
<pre>
+
<source lang="cpp">
 
   struct piwo
 
   struct piwo
 
   {
 
   {
Linia 96: Linia 99:
 
     p.czyDobre = true;  
 
     p.czyDobre = true;  
 
   }
 
   }
</pre>
+
</source>
  
 
Metody są fajne. Mogą dużo zrobić, a my dzięki ich wywołaniom możemy łatwo nimi zarządzać. Zauważ, że np. ''string'' jest czymś podobnym do struktury. Ciężko jest dostać się do jego pól, ale za to ma wiele metod, np: <code>length(), size(), erase(...), insert(...)</code>. Jeśli chcesz poczytać więcej o ''stringach'', to zapraszam [http://www.sgi.com/tech/stl/basic_string.html tutaj].
 
Metody są fajne. Mogą dużo zrobić, a my dzięki ich wywołaniom możemy łatwo nimi zarządzać. Zauważ, że np. ''string'' jest czymś podobnym do struktury. Ciężko jest dostać się do jego pól, ale za to ma wiele metod, np: <code>length(), size(), erase(...), insert(...)</code>. Jeśli chcesz poczytać więcej o ''stringach'', to zapraszam [http://www.sgi.com/tech/stl/basic_string.html tutaj].
Linia 102: Linia 105:
 
=== Parametry const ===
 
=== Parametry const ===
 
W strukturach można zagwarantować, że metoda nic nie namiesza. Robi się to dopisując '''const''' pzed definicją metody. Zobacz:
 
W strukturach można zagwarantować, że metoda nic nie namiesza. Robi się to dopisując '''const''' pzed definicją metody. Zobacz:
 +
<source lang="cpp">
 
   struct wektor
 
   struct wektor
 
   {
 
   {
Linia 112: Linia 116:
 
     }
 
     }
 
   };
 
   };
 
+
</source>
 
== Konstruktory ==
 
== Konstruktory ==
 
Konstruktorem struktury jest metoda wywoływana przy kontrukcji struktury (deklaracji).
 
Konstruktorem struktury jest metoda wywoływana przy kontrukcji struktury (deklaracji).
 
Na przykład pisząc
 
Na przykład pisząc
 +
<source lang="cpp">
 
   punkt Q;
 
   punkt Q;
 +
</source>
 
Tworzy się punkt i wywołuje się  '''domyślny konstruktor'''. Czasem chciałoby się powiedzieć jakie mają dostać wartości poszególne pola strktury. Dlatego w tym celu należy napisać własny konstruktor (niekoniecznie domyślny):
 
Tworzy się punkt i wywołuje się  '''domyślny konstruktor'''. Czasem chciałoby się powiedzieć jakie mają dostać wartości poszególne pola strktury. Dlatego w tym celu należy napisać własny konstruktor (niekoniecznie domyślny):
<pre>
+
<source lang="cpp">
 
   struct pralka
 
   struct pralka
 
   {
 
   {
Linia 138: Linia 144:
 
     }
 
     }
 
   };
 
   };
</pre>
+
</source>
 
Teraz w funkcji np. main mozemy konstruować zmienne typu 'pralka' za pomoca naszych konstruktorów:
 
Teraz w funkcji np. main mozemy konstruować zmienne typu 'pralka' za pomoca naszych konstruktorów:
<pre>
+
<source lang="cpp">
 
   pralka P; //tutaj wywołuje sie domyślny konstruktor.
 
   pralka P; //tutaj wywołuje sie domyślny konstruktor.
 
   pralka T(800); // teraz T.ileObrotow == 800  :-)
 
   pralka T(800); // teraz T.ileObrotow == 800  :-)
  
 
   pralka tablica[1000]; // konstruktor domyślny wywołuje się aż 1000 razy
 
   pralka tablica[1000]; // konstruktor domyślny wywołuje się aż 1000 razy
</pre>
+
</source>
  
 
== Operatory ==
 
== Operatory ==
Linia 151: Linia 157:
 
Ale to nie wszystko. Przypomnij sobie co można było robić więcej ze stringami :-).
 
Ale to nie wszystko. Przypomnij sobie co można było robić więcej ze stringami :-).
 
Można było zrobić coś takiego
 
Można było zrobić coś takiego
 +
<source lang="cpp">
 
   string A = "ala ";
 
   string A = "ala ";
 
   string B = "ma kota";
 
   string B = "ma kota";
 
   string C = A+B+A+A+B;
 
   string C = A+B+A+A+B;
 +
</source>
 
Czy to nie jest super? Jacyś kolesie napisali operator dodawania dla stringów. Czemu by nie napisać własny operator dla swojej struktury? Robi się to tak:
 
Czy to nie jest super? Jacyś kolesie napisali operator dodawania dla stringów. Czemu by nie napisać własny operator dla swojej struktury? Robi się to tak:
<pre>
+
<source lang="cpp">
 
   struct wektor
 
   struct wektor
 
   {
 
   {
Linia 175: Linia 183:
 
     }
 
     }
 
   }
 
   }
</pre>
+
</source>
 
Zobaczmy, jak działa to w praktyce:
 
Zobaczmy, jak działa to w praktyce:
<pre>
+
<source lang="cpp">
 
   wektor A(1,2);
 
   wektor A(1,2);
 
   wektor B(3,4);
 
   wektor B(3,4);
Linia 183: Linia 191:
 
   wektor C = A+B;
 
   wektor C = A+B;
 
   wektor D = A*7; // niestety nie można pisać  7*A, bo int-y nie mają operatora '*' mnożenia przez wektor.
 
   wektor D = A*7; // niestety nie można pisać  7*A, bo int-y nie mają operatora '*' mnożenia przez wektor.
</pre>
+
</source>
  
 
=== Operatory porównywania ===
 
=== Operatory porównywania ===
 
Znowy odwołam się do stringów, bo to naprawdę świetnie napisana struktura. Nie wiem, czy wiecie, ale stringi można porównywać
 
Znowy odwołam się do stringów, bo to naprawdę świetnie napisana struktura. Nie wiem, czy wiecie, ale stringi można porównywać
 +
<source lang="cpp">
 
   string A = "ala ma", B = "aleksander";
 
   string A = "ala ma", B = "aleksander";
 
   if (A < B) { ... } // sprawdzenie, czy A jest mniejszy leksykograficznie niż B
 
   if (A < B) { ... } // sprawdzenie, czy A jest mniejszy leksykograficznie niż B
 +
</source>
 
To naprawdę wygodne narzędzie
 
To naprawdę wygodne narzędzie
 
No więc do dzieła. Napiszmy własny operator&lt;
 
No więc do dzieła. Napiszmy własny operator&lt;
<pre>
+
<source lang="cpp">
 
   struct wektor
 
   struct wektor
 
   {
 
   {
Linia 204: Linia 214:
 
     }
 
     }
 
   }
 
   }
</pre>
+
</source>
  
 
=== Sortowanie ===
 
=== Sortowanie ===
Linia 210: Linia 220:
  
 
Ten przykład wyjaśnia jak należy pisać operator mniejszości, aby móc potem sortować za pomocą funkcji ''sort'' z bilbioteki '''STL''':
 
Ten przykład wyjaśnia jak należy pisać operator mniejszości, aby móc potem sortować za pomocą funkcji ''sort'' z bilbioteki '''STL''':
<pre>
+
<source lang="cpp">
 
   struct wektor
 
   struct wektor
 
   {
 
   {
Linia 246: Linia 256:
 
     return 0;
 
     return 0;
 
   }
 
   }
</pre>
+
</source>

Wersja z 09:45, 5 gru 2007

Spis treści

Podstawy

Jak już wiemy, w języku C/C++ zdefiniowanych jest wiele typów danych, np. int, long long, char, string, void, float, double. Jeśli jest to dla nas zbyt mało, to możemy zdefiniować swój własny typ złożony. Nazywamy go strukturą. Na przykład typ będący parą dwóch zmiennych int-a i string-a, w języku C++ definiujemy następująco:

struct para
  {
    int n;
    string s;
  };

Teraz możemy deklarować zmienne typu para (nazwę można było wpisać prawie dowolną) w następujący sposób:

 para X;

Mamy wówczas zmienną X.

Pola struktury

Do pól struktury możemy dostać się po kropce, mianowicie (kontynuujemy poprzedni przykład)

int a;
  a = X.n;
  cin >> X.s; // wczytywanie string-a ze standardowego wejścia

Możemy także napisać własną funkcję, która zwraca zmienną typu para, np.

para funkcja(string argument)
  {
    para wynik;
    wynik.n = argument.length();
    wynik.s = argument;
    return wynik;     // ważne jest, że wynik jest typu 'para'
  }

Metody

Struktura może mieć nie tylko pola (zmienne pewnego typu); można także dla struktry zdefiniować tzw. 'metody, czyli funkcje obsługujące interfejs struktury. Na przykład

struct punkt
  {
    int x; // to jest pole
    int y; // to jest pole
 
    bool czyLezyNaOsi() // metoda, a wiec funkcja, ktora nie pobiera zadnych argumentow; odpowiada czy punkt lezy na jednej z osi OX lub OY
    {
       if (x==0) return true;
       if (y==0) return true;
       return false;
    }
 
    int ktoraCwiartka() // funkcja zwraca numer cwiartki, do ktorej nalezy punkt
    {
       if (czyLezyNaOsi()) return 0;
       if (x>0 && y>0) return 1;
       if (x<0 && y>0) return 2;
       if (x<0 && y<0) return 3;
       if (x>0 && y<0) return 4;
    }
  }; // koniec definicji struktury musi zawierać średnik

Teraz możemy poszaleć:

punkt tablica[100]; // deklarujemy tablice struktur punkt
  for (int i=0; i<100; i++) // wczytujemy 100 punktow
    cin >> tablica[i].x >> tablica[i].y;
 
  int ile[5] = { 0,0,0,0,0 }; // deklarujemy tablice pięciu zer
  for (int i=0; i<100; i++)
  {
    int cw = tablica[i].ktoraCwiartka(); // do metody struktury także dostajemy się po kropce
    ile[cw] = ile[cw]+1; // zwiekszamy liczbe punktów należących do ćwiartki nr 'cw'
  }
 
  cout << " W cwiartce nr 1 jest " << ile[1] << " punktow." << endl;
  cout << " W cwiartce nr 2 jest " << ile[2] << " punktow." << endl;
  cout << " W cwiartce nr 3 jest " << ile[3] << " punktow." << endl;
  cout << " W cwiartce nr 4 jest " << ile[4] << " punktow." << endl;

Po co to? Otóz metody są bardzo przydatne. Mogą coś dla nas zrobić tak jak funkcje. Należy rozumieć, że tutaj każda zmienna (struktura) ma swoją własną funkcję. Jeśli w definicji metody używamy zmiennych o nazwach takich, jak jej pola to wówczas odwołujemy się do pól struktury. Metody mogą więc nieźle namieszać w strukturze. Kolejny przykład:

struct piwo
  {
    int ileProcent;
    int ileChmielu;
    bool czyCiemne;
    bool czyDobre;
 
    void dobre(bool czy)
    {
      czyDobre = czy;
    }
  };
 
  int main(void)
  {
    piwo P;
    p.dobre(true); // używamy metody dobre, aby ustawić pole 'czyDobre' na true;
    // ten sam efekt moglibyśmy uzyskać pisząc
    p.czyDobre = true; 
  }

Metody są fajne. Mogą dużo zrobić, a my dzięki ich wywołaniom możemy łatwo nimi zarządzać. Zauważ, że np. string jest czymś podobnym do struktury. Ciężko jest dostać się do jego pól, ale za to ma wiele metod, np: length(), size(), erase(...), insert(...). Jeśli chcesz poczytać więcej o stringach, to zapraszam tutaj.

Parametry const

W strukturach można zagwarantować, że metoda nic nie namiesza. Robi się to dopisując const pzed definicją metody. Zobacz:

struct wektor
  {
    int x,y;
 
    float length() const         // dopisanie const powoduje, że kompilator nie dopuści np do czegoś takiego : x = 5;
                                 // po prostu nie wolno nam zmieniac wartości wszystkich pól struktury!
    {
      return sqrt( x*x + y*y );
    }
  };

Konstruktory

Konstruktorem struktury jest metoda wywoływana przy kontrukcji struktury (deklaracji). Na przykład pisząc

punkt Q;

Tworzy się punkt i wywołuje się domyślny konstruktor. Czasem chciałoby się powiedzieć jakie mają dostać wartości poszególne pola strktury. Dlatego w tym celu należy napisać własny konstruktor (niekoniecznie domyślny):

struct pralka
  {
    int ileObrotow;
    bool czyNowa;
    int cena;
 
    pralka() // konstruktor domyślny
    {
      czyNowa = true;
      ileObrotow = 0;
      cena = 0;
    }
 
    pralka(int obroty) // knstruktor pobierajacy jednego int-a
    {
      czyNowa = true;
      ileObrotow = obroty;
    }
  };

Teraz w funkcji np. main mozemy konstruować zmienne typu 'pralka' za pomoca naszych konstruktorów:

pralka P; //tutaj wywołuje sie domyślny konstruktor.
  pralka T(800); // teraz T.ileObrotow == 800  :-)
 
  pralka tablica[1000]; // konstruktor domyślny wywołuje się aż 1000 razy

Operatory

Mam nadzieję, że spodobały Ci się nowe możliwości, jakie dają Ci struktury w C/C++. Ale to nie wszystko. Przypomnij sobie co można było robić więcej ze stringami :-). Można było zrobić coś takiego

string A = "ala ";
  string B = "ma kota";
  string C = A+B+A+A+B;

Czy to nie jest super? Jacyś kolesie napisali operator dodawania dla stringów. Czemu by nie napisać własny operator dla swojej struktury? Robi się to tak:

struct wektor
  {
    int x, y; // wspołrzędne wektora
    wektor(int xx, int yy) { x = xx; y = yy; } // wygodny konstruktor, np.  wektor W(4,5);
    wektor() { x = 0; y = 0; }                 // domyślny konstruktor
 
    wektor operator* (int skalar) // operator mnożenia wektor przez liczbę
    {
      return wektor(x*skalar, y*skalar); // zwracamy to, co skonstruuje konstruktor
    }
 
    wektor operator+ (wektor W) // operator dodawania wektora W do wektora
    {
      wektor wynik; // nie potrzeba, ale mozemy skonstruowac zmienną, którą potem zwrócimy jako wynik funkcji
      wynik.x =  x + W.x;
      wynik.y =  y + W.y;
      return wynik;
    }
  }

Zobaczmy, jak działa to w praktyce:

wektor A(1,2);
  wektor B(3,4);
 
  wektor C = A+B;
  wektor D = A*7; // niestety nie można pisać  7*A, bo int-y nie mają operatora '*' mnożenia przez wektor.

Operatory porównywania

Znowy odwołam się do stringów, bo to naprawdę świetnie napisana struktura. Nie wiem, czy wiecie, ale stringi można porównywać

string A = "ala ma", B = "aleksander";
  if (A < B) { ... } // sprawdzenie, czy A jest mniejszy leksykograficznie niż B

To naprawdę wygodne narzędzie No więc do dzieła. Napiszmy własny operator<

struct wektor
  {
    int x, y; // wspołrzędne wektora
    wektor(int xx, int yy) { x = xx; y = yy; } // wygodny konstruktor, np.  wektor W(4,5);
 
    bool operator< (wektor W)   // "porównanie dwóch wektorych według porządku na współrzędnych (leksykograficznie)
    {
      if (x<W.x) return true;
      if (x>W.x) return false;
      return y<W.y;  // tak też można; w końcu y<W.y ma jakąś wartość logiczną, którą zwracamy tutaj jako wynik operatora<
    }
  }

Sortowanie

Szerzej o sortowaniu mówimy w arytkule Sortowanie. Tutaj poświęcimy uwage, jak napisać operator porównywania, aby móc sortować tablice naszych własnych struktur.

Ten przykład wyjaśnia jak należy pisać operator mniejszości, aby móc potem sortować za pomocą funkcji sort z bilbioteki STL:

struct wektor
  {
    int x, y; // wspołrzędne wektora
    wektor(int xx, int yy) { x = xx; y = yy; } // wygodny konstruktor, np.  wektor W(4,5);
    wektor() { x = 0; y = 0; }                 // domyślny konstruktor
 
    wektor operator* (int skalar) // operator mnożenia wektor przez liczbę
    {
      return wektor(x*skalar, y*skalar); // zwracamy to, co skonstruuje konstruktor
    }
 
    wektor operator+ (wektor W) // operator dodawania wektora W do wektora
    {
      wektor wynik; // nie potrzeba, ale mozemy skonstruowac zmienną, którą potem zwrócimy jako wynik funkcji
      wynik.x =  x + W.x;
      wynik.y =  y + W.y;
      return wynik;
    }
 
    bool operator< (const wektor &W) const  // takiego operatora wymaga funkcja sort z biblioteki algorithms
    {
      if (x<W.x) return true;
      if (x>W.x) return false;
      return y<W.y;
    }
  };
 
  int main void()
  {
    wektor tab[1000];
    for (int i=0; i<1000; i++) cin >> tab[i].x >> tab[i].y; // wczytywanie wektorów (każdy dany jako dwie liczby  int)
 
    sort(tab, tab+1000); // sortowanie :-) (wszystko układa się od najmniejszego do największego za pomocą operatora <
    return 0;
  }
Osobiste