## Jacek Matulewski
Tworzenie klas w C++, Javie, Object Pascalu i C#
wersja 1.0

W serii przedstawionych poniżej kilkunastu kroków zaprezentowałem typowe postępowanie przy projektowaniu klasy od deklaracji jej pól i metod, aż do omówienia dziedziczenia. Klasa służąca jako przykład to implementacja ułamka zwykłego, który każdy zna dobrze z pierwszych klas szkoły podstawowej np. 1/2 lub 3/4.

#### Spis treści:

1. Deklaracja klasy, pola
2. Deklaracja klasy, metody
3. Obsługa błędów w konstruktorze klasy wykorzystująca wyjątki
4. Konstruktor
5. Własne klasy wyjatków
6. Konstruktor copy
7. Obiekty stałe
8. Działania określone dla klasy, operatory
9. Operatory/funkcje konwersji
10. Metoda "uproszczenie ułamka"
11. Rozdzielenie deklaracji i definicji klasy, pliki nagłówkowy i zródłowy
12. Szablon klasy
13. Dziedziczenie publiczne

Źródła: Borland C++ Builder 6/JBuilder 6 Personal/Borland Delphi 6 Personal/Borland C# Builder 1

Język programowania:

Uwaga! Mechanizm wyboru języka wymaga zapisania cookie.

Uwaga! (C++)
Kod klasy ulamek pisany jest w czystym C++ i jest w pełni przenaszalny (można zastosować inne kompilatory np. gcc, także pod Linuxem). Przykłady ilustrujące wykorzystanie opisywanej tu klasy korzystają z funkcji WinAPI ShowMessage() i są napisane w C++ Builder 6. Funkcję tą można z powodzeniem zastąpić przekazywaniem tekstu do stanardowego strumienia wyjścia. Tzn. wszędzie tam, gdzie znajduje się jej wywołanie, np.:

    ShowMessage((AnsiString)"a: "+licznik+"/"+mianownik);

należy umieścić:

    srd::cout << "a: " << licznik << "/" << mianownik << "\n";

W tym przypadku należy pamiętać o uwzględnieniu odpowiedniej biblioteki: #include <iostream>
Wyjątkiem jest ostatnia klasa, która korzysta z AnsiString, który nie jest elementem standardowym C++.

Uwaga! (Delphi)
Kod klasy Ulamek wykorzystuje elementy Object Pascala, które zostały wprowadzone od wersji 6 (m.in przeciążanie metod). W przykładach zostały podane metody związane ze zdarzeniem Button1.OnClick. Mimot to bez większych kłopotów, po usunięciu niektórych elementów kod klasy powinien się kompilować także w Turbo Pascalu.

Uwaga! (Java)

- Przykłady ilustrujące wykorzystanie klasy Ulamek wymagają umieszczenia na formie obiektu java.awt.List (w JBuilderze komponent ten znajduje się na zakładce AWT). Jeżeli aplet korzysta z biblioteki Swing można wykorzystać statyczną metodę JOptionPane.showMessageDialog(this, "Tekst komunikatu");
- Przykłady umieszczone w źródłach zostały napisane za pomocą JBuilder 6 Personal, ale można je również skompilować niezależnie (niezbędny jest wówczas Java Development Kit (JDK), który można ściągnąć ze strony http://java.sun.com). W tym celu należy skopiować z wybranego projektu katalog zawierający cały pakiet klasy ("src\klasy"), a następnie w katalogu nadrzędnym (katalog "klasy" musi bć skopiowany jako całość, jego nazwa musi być zachowana) uruchomić kompilator poleceniem (zazwyczaj niezbędne jest podanie pełnej ścieżki do javac.exe lub dopisanie jej do zmiennej środowiskowej PATH):

      javac klasy\*.java

  Aplikację można teraz uruchomić wpisując:

      java klasy.Application1

  Można się posunąć o krok dalej i stworzyć pojedynczy plik wykonywalny jar. Należy przygotować prosty plik (np. "klasyManifest") zawierający nazwę wykonywalnej klasy w jednej linii zakończonej znakiem końca linii (w naszym przypadku linia ta powinna zawierać: Main-Class: klasy.Application1). Teraz można spakować skompilowane klasy poleceniem:

      jar cmf klasyManifest klasy.jar klasy\*.class

  i uruchomić je klikając w Windows Explorerze dwukrotnie myszką na plik klasy.jar lub wpisując:

      java -jar klasy.jar

  Uwaga!
  W przypadku wszystkich przykładów najlepiej na formie umieścić przycisk i wkleić kod do ich metod zdarzeniowych związanych z jego przyciśnięciem.

  #### Deklaracja klasy, pola:

  C++
      class ulamek
      	{
          private:
          	int licznik, mianownik;
          };
  | Java (plik Ulamek.java)
      public class Ulamek
      {
      	private int licznik,mianownik;
      }
  | Delphi
      interface

      type
      ulamek = class
      	private
          	licznik,mianownik :Integer;
          end;
  | C#
      public class Ulamek
      {
      	private int licznik,mianownik;
      }
  Przykład kodu:
      ulamek a;
      ulamek* b=new ulamek();

      delete b;
  | Przykład kodu:
      Ulamek a=new Ulamek();
  | Przykład kodu:
      procedure TForm1.Button1Click(Sender: TObject);
      var a :Ulamek;

      begin
      a:=Ulamek.Create;
      a.Free;
      end;
  | Przykład kodu:
      Ulamek a=new Ulamek();

  Uwagi!

  - C++, Java: Brak konstruktora powoduje utworzenie konstruktora domyślnego (bez argumentów)
  - C++: Obiekt może być stworzony statycznie (pierwsza linia przykładu) - wówczas trafia na stos (ang. stack), w przeciwieństwie do obiektów tworzonych dynamicznie za pomocą operatora new (druga linia przykładu), dla których rezerwowana (alokowana) jest pamięć na stercie (ang. heap). W większości kompilatorów wielkość stosu można zmianiać, ale jest to zwykle wielkość rzędu kilkudziesięciu kilobajtów. W przeciwieństwie do ograniczonego stosu, sterta jest znacznie pojemniejsza. Adres obiektu na stercie przechowywany jest we wskaźnikach do objektu (b w przykładzie).
  - Java: Tylko jedna klasa w pliku może być zadeklarowana jako public. Jeżeli kod klasy Ulamek umieszczony jest w tym samym pliku, co kod testującej aplikacji, należy usunąć słowo kluczowe public sprzed deklaracji, ponieważ tak zadeklarowama musi być klasa okna lub appletu. Jeżeli w pliku znajduje się klasa publiczna, to jej nazwa musi być identyczna jak nazwa pliku (bez rozszerzenia).
  - Java: W Javie nie ma operatorów referencji. Jednak zmienna, którą deklarujemy podając typ klasy jest w istocie referencją, z którą operator new wiąże stworzony obiekt.
  - Java, Pascal: Częstym źródłem błędów w Pascalu i Javie jest pojęcie zmiennej obiektowej (zmiennej przechowującej referencję do obiektu). Należy pamiętać, że zadeklarowanie zmiennej typu klasy (np. Ulamek a) nie oznacza jeszcze utworzenia obiektu i przydzielenia pamięci, a jedynie zadeklarowanie zmiennej, który na taki obiekt będzie wskazywała (natomiast w C++ byłoby to już statyczne stworzenie obiektu). Do stworzenia (i zainicjowania) obiektu konieczne jest jeszcze wywołanie konstruktora (Ulamek a=new Ulamek() lub var a: Ulamek; a:=Ulamek.Create(). Obiekt jest automatycznie usuwany z pamięci jeżeli wątek wyjdzie poza zakres jego zmiennej referencyjnej.
  - Delphi: Obiekt nie dziedziczący z żadnego obiektu w deklaracji, w istocie dziedziczy z TObject (zawiera zatem konstruktor Create i destruktor Destroy i jeszcze kilka innych metod). Deklaracja mogłaby zaczynać się od ulamek = class(TObject). Nie jest zalecane bezpośrednie wywoływanie destruktora, lepiej wywołać metodę Free.
  - Java: Jeżeli w deklaracji klasy nie ma jawnie wymienionego przodka, klasa dziedziczy z systemowej klasy Object. Zatem nawet pusta klasa posiada już kilka metod (clone() - odpowiednik konstruktora kopiującego, equals() - odpowiednik operatora porównania ==, toString()).
  - Java: Ponieważ w Javie nie ma konwersji z typów prostych (np. int) na obiekty (np. String), należy korzystać z obiektów opakowujących (np. Integer.toString()) lub metod obiektu docelowego (np. String.valueOf()).
  - C#: Do implementacji ułamka zwykłego lepsze byłoby zdefiniowanie struktury zamiast klasy (słowo kluczowe class należałoby zastąpić słowem struct). Obiekty struktur w C# tworzone są na stosie (ang. stack), podczas gdy klas na stercie (ang. heap). Ponadto w przypadku struktur nie można korzystać z dziedziczenia.

  #### Deklaracja klasy, metody:

  1. Definicja klasy - słowo kluczowe class i blok następujący bespośrednio za nim: class {...}
  2. Deklaracja klasy - zbiór metod określonych poza definicją klasy.
     Uwaga! Funkcje zadeklarowane w obrębie definicji klasy są zawsze inline, co oznacza, że są włączane do kodu binarnego w każdym miejscu wywołania metody i nie podlegają optymalizacji.
  3. Interface klasy - część publiczna klasy, służąca do komunikacji z innymi obiektami i elementami kodu.
  4. Implementacja klasy - część pryatna i chroniona, która realizuje właściwe zadania klasy.

  C++
      class ulamek
      	{
      	private:
      		int licznik, mianownik;
      	public:
      		bool set(int alicznik,int amianownik)
      			{
      			if (amianownik==0) return false;
      			licznik=alicznik;
      			mianownik=amianownik;
      			return true;
      			}
      		void get(int& rlicznik,int& rmianownik) const
      			{
      			rlicznik=licznik;
      			rmianownik=mianownik;
      			}
      		void get(int* plicznik,int* pmianownik) const
      			{
      			*plicznik=licznik;
      			*pmianownik=mianownik;
      			}
      	};
  | Java
      package klasy;

      /**
       * <p>Title: Ulamek</p>
       * <p>Description: Przykladowa klasa Ulamek</p>
       * <p>Copyright: Copyright (c) Jacek Matulewski 2003</p>
       * <p>Company: </p>
       * @author unascribed
       * @version 1.0
       */

      public class Ulamek
      {
        private int licznik,mianownik;

        public boolean set(int alicznik,int amianownik)
          {
          if (amianownik==0) return false;
          licznik=alicznik;
          mianownik=amianownik;
          return true;
          }

        public void get(int[] arg)
          {
          arg[0]=licznik;
          arg[1]=mianownik;
          }

        public int[] get()
          {
          int[] wynik=new int[2];
          wynik[0]=licznik;
          wynik[1]=mianownik;
          return wynik;
          }
      }
  | Delphi
      unit _ulamek;

      interface

      type
      Ulamek = class
          private
              licznik :Integer;
              mianownik :Integer;
          public
              function SetValues(const alicznik, amianownik :Integer) :Boolean;
              procedure GetValues(var rlicznik :Integer; var rmianownik :Integer); overload;
              procedure GetValues(plicznik, pmianownik :PInteger); overload;
          end;

      implementation

      function Ulamek.SetValues(const alicznik, amianownik :Integer) :Boolean;
      begin
      if amianownik=0 then begin
                           SetValues:=False;
                           Exit;
                           end;
      licznik:=alicznik;
      mianownik:=amianownik;
      SetValues:=True;
      end;

      procedure Ulamek.GetValues(var rlicznik :Integer; var rmianownik :Integer);
      begin
      rlicznik:=licznik;
      rmianownik:=mianownik;
      end;

      procedure Ulamek.GetValues(plicznik :PInteger; pmianownik :PInteger);
      begin
      plicznik^:=licznik;
      pmianownik^:=mianownik;
      end;

      end.
  | C#
      public class Ulamek
      {
      	private int licznik,mianownik;

      	public bool set(int alicznik,int amianownik)
      		{
      		if (amianownik==0) return false;
      		licznik=alicznik;
      		mianownik=amianownik;
      		return true;
      		}

      	public void get(out int rlicznik,out int rmianownik)
      		{
      		rlicznik=licznik;
      		rmianownik=mianownik;
      		}

      	public void get(int[] arg)
      		{
      		arg[0]=licznik;
      		arg[1]=mianownik;
      		}

      	public int[] get()
      		{
      		int[] wynik=new int[2];
      		wynik[0]=licznik;
      		wynik[1]=mianownik;
      		return wynik;
      		}
      }
  Przykład kodu:
      ulamek a;
      if (a.set(1,2)) ShowMessage("a,1: OK"); else ShowMessage("a,1: Blad");
      if (a.set(1,0)) ShowMessage("a,2: OK"); else ShowMessage("a,2: Blad");

      ulamek* b=new ulamek();
      if (b->set(3,4)) ShowMessage("b: OK"); else ShowMessage("b: Blad");

      int licznik,mianownik;
      a.get(licznik,mianownik);
      ShowMessage((AnsiString)"a: "+licznik+"/"+mianownik);

      (*b).get(&licznik,&mianownik);  //b->get() == (*b).get()
      ShowMessage((AnsiString)"b: "+licznik+"/"+mianownik);

      delete b;
  | Przykład kodu:
      Ulamek a=new Ulamek();

      if (a.set(1,2)) list1.add("a,1: OK"); else list1.add("a,1: Blad");
      if (a.set(1,0)) list1.add("a,2: OK"); else list1.add("a,2: Blad");

      int[] licznik_mianownik=new int[2];
      a.get(licznik_mianownik);
      list1.add("a: "+String.valueOf(licznik_mianownik[0])+"/"+
                      String.valueOf(licznik_mianownik[1]));

      licznik_mianownik=a.get();
      list1.add("a: "+String.valueOf(licznik_mianownik[0])+"/"+
                      String.valueOf(licznik_mianownik[1]));
  | Przykład kodu:
      procedure TForm1.Button1Click(Sender: TObject);
      var a,b :Ulamek;
          licznik,mianownik :Integer;
      begin
      a:=Ulamek.Create;
      if a.SetValues(1,2) then ShowMessage('a,1: OK') else ShowMessage('a,1: Blad');
      if a.SetValues(1,0) then ShowMessage('a,2: OK') else ShowMessage('a,2: Blad');

      b:=Ulamek.Create;
      if b.SetValues(3,4) then ShowMessage('b: OK') else ShowMessage('b: Blad');

      a.GetValues(licznik,mianownik);
      ShowMessage('a: '+IntToStr(licznik)+'/'+IntToStr(mianownik));

      b.GetValues(@licznik,@mianownik);
      ShowMessage('b: '+IntToStr(licznik)+'/'+IntToStr(mianownik));

      a.Free;
      b.Free;
      end;
  | Przykład kodu:
      Ulamek a=new Ulamek();

      if (a.set(1,2)) MessageBox.Show("a,1: OK"); else MessageBox.Show("a,1: Blad");
      if (a.set(1,0)) MessageBox.Show("a,2: OK"); else MessageBox.Show("a,2: Blad");

      int licznik=0,mianownik=0;
      a.get(out licznik,out mianownik);
      MessageBox.Show("a: "+licznik+"/"+mianownik);

      int[] licznik_mianownik=new int[2];
      licznik_mianownik=a.get();
      MessageBox.Show("a: "+licznik_mianownik[0]+"/"+licznik_mianownik[1]);

  Uwaga!
  - C++: Definiowanie meotdy w obrębie deklaracji powoduje, że jest to metoda inline, co powoduje wstawienie całej "treści" funkcji w każdym miejscu jej wywołania. Identyczny efekt można uzyskać dodając do definicji słowo kluczowe inline
  - C++: Metoda get została przeciążona - możliwe jest pobieranie wartości licznika i mianownika za pośrednictwem wskaźnika lub przez referencje.
  - Delphi: Pascal posiada słowo kluczowe set i dlatego nazwy funkcji nie mogą się nazywać tak jak w przykładach z C++ i Javy.
  - Delphi: Aby przeciążyć funkcje w Object Pascalu niezbędne jest jawne użycie słowa kluczowego overload. W starszych wersjach Object Pascala (np. Delhi 3) przeciążanie metod nie było możliwe.
  - Delphi: Wskaźniki nie są w Pascalu czymś równie "naturalnym" jak w C++. Typy wbudowane mają zmienne typu wskaźnikowego budowane przez dodanie P do nazwy np. PInteger.
  - Java: W Javie nie ma operatorów wskaźników ani referencji. Natomiast należy wiedzieć, że referencjami są nazwy tablic i obiektów. W konsekwencji argumenty funkcji, które są typami prostymi podawane są zawsze przez wartość, a tablic i obiektów - przez referencję. Stąd najprostszą sztuczką, żeby argument zwracał wartość typu prostego jest utworzenie jednoelementowej tablicy i przekazywanie jej (a dokładnie jej referencji) przez argument.
  - C#: Przekazywanie wartości przez referencję realizowane jest za pomocą słowa kluczowego ref lub out. W drugim przypadku wartość argumentu musi być zmieniona.
  - C#: Można wykorzystać także wskaźniki, ale wówczas konieczna jest kompilacja z opcją /unsafe i zaznaczanie metod korzystających ze wskaźników modyfikatorem unsafe:

        unsafe public void get(int* plicznik,int* pmianownik)
        	{
        	*plicznik=licznik;
        	*pmianownik=mianownik;
        	}

  #### Obsługa błędów w konstruktorze klasy wykorzystująca wyjątki:

  Polecenie throw w C++ i Javie oraz raise w Delphi powoduje wysłanie wyjątku. Rezultatem jest natychmiastowe przeniesienie do sekcji catch związanego z otaczającym wystąpienie wyjątku polecenia try. Jeżeli taka sekcja nie zostanie znaleziona, lub nie przyjmuje klasy obiektu, który został przesłany jako wyjątek - wówczas obsługę wyjątku przejmuje domyślna obsługa wyjątków (w C++ Builderze jest to try ... catch otaczające Application->Run, w Javie - wywołana zostanie metoda UncaughtException). Wyjątki zastępują obsługę błędów polegającą na zwracaniu wartości logicznej przez każdą funkcję. Pozwala to na unknięcie sprawdzania wartości funkcji przy każdym jej wywołaniu, szczególnie w wywołaniach zagnieźdzonych oraz na obsługę błędów w konstruktorze, który żadnej wartości zwracać nie może.

  Najbardziej konsekwętnie obsługa wyjątków przeprowadzona została w Javie, gdzie metoda przesyłająca wyjątek musi deklarować taką możliwość w nagłówku, za nazwą i argumentami, a kompilator pozwala na wywołanie takiej metody tylko po otoczeniu przez polecenia przechwytujące wyjątki.

  Uwaga!
  Po przesłaniu wyjątku i wykonaniu bloku instrukcji zawartych w catch program nie wraca do linii następującej po wystopięniu wyjątku.

  C++
      void set(int alicznik,int amianownik)
      	{
      	if (amianownik==0) throw "Ulamek: Blad #1";
      	licznik=alicznik;
      	mianownik=amianownik;
      	}
  | Java
      public void set(int alicznik,int amianownik)
          throws Exception
          {
          if (amianownik==0) throw new Exception("Ulamek: Blad #1");
          licznik=alicznik;
          mianownik=amianownik;
          }
  | Pascal
      procedure Ulamek.SetValues(const alicznik, amianownik :Integer);
      begin
      if amianownik=0 then raise Exception.Create('Ulamek: Blad #1');
      licznik:=alicznik;
      mianownik:=amianownik;
      end;
  | C#
      public void set(int alicznik,int amianownik)
      	{
      	if (amianownik==0) throw new Exception("Ulamek: Blad #1");
      	licznik=alicznik;
      	mianownik=amianownik;
      	}
  Przykład kodu:
      ulamek* c=new ulamek();
      try
          {
          c->set(1,0);
          ShowMessage("c: OK");
          }
      catch(char* E) {ShowMessage(E);}

      int licznik,mianownik;
      c->get(licznik,mianownik);
      ShowMessage((AnsiString)"c: "+licznik+"/"+mianownik);

      delete c;
  | Przykład kodu:
          Ulamek c=new Ulamek();

          try
            {
            c.set(1,0);
            list1.add("c: OK");
            }
          catch(Exception E)
            {
            list1.add(E.getMessage());
            }

          int[] licznik_mianownik=new int[2];
          c.get(licznik_mianownik);
          list1.add("c: "+String.valueOf(licznik_mianownik[0])+"/"+
                    String.valueOf(licznik_mianownik[1]));
  | Przykład kodu:
      procedure TForm1.Button1Click(Sender: TObject);
      var c :Ulamek;
          licznik,mianownik :Integer;
      begin
      c:=Ulamek.Create;

      try
        c.SetValues(1,0);
        ShowMessage('c: OK');
      except
        on E: Exception do ShowMessage(E.Message);
      end;

      c.GetValues(licznik,mianownik);
      ShowMessage('c: '+IntToStr(licznik)+'/'+IntToStr(mianownik));

      c.Free;
      end;
  | Przykład kodu:
      Ulamek c=new Ulamek();

      try //Obsluga wyjatkow w C# nie jest obowiazkowa
      {
      	MessageBox.Show("Pojawi się wyjątek, bo mianownik ustalam na 0");
      	c.set(1,0);
      	MessageBox.Show("c: OK");
      }
      catch(Exception exc)
      {
      	MessageBox.Show(exc.Message);
      }

      int licznik,mianownik;
      c.get(out licznik,out mianownik);
      MessageBox.Show("c: "+licznik+"/"+mianownik);

  Uwaga!
  - C++: c->get() jest rownoznaczne z (*c).get()
  - C++: Wyjatki weszly do standardu C++ dopiero w 1996 roku. Niektore starsze kompilatory moga ich nie obsługiwać lub nie spełniać wpełni standardu (np. brak obiektu std:exception w C++ Builder 1). W nowszych kompilatorach bardzo można sobie ułatwić zadanie wykorzystując np. funkcję std::runtime_error("komunikat") (w tym celu należy załadować bibliotekę standardową i pamiętać o używaniu namespace'u std: tj. linii: #include <stdexcept> oraz using namespace std;)
  - Delphi: W Object Pascalu można jako wyjątki przesyałać jedynie obiekty.
  - Java, Delphi: Odpowiednikiem wyłapywania wszystkich wyjątków przez catch(...) w C++ jest słowo kluczowe final w Javie i Object Pascalu.

  #### Konstruktor:

  C++
      class ulamek
      	{
          private:
          	int licznik, mianownik;
          public:
          	ulamek(int alicznik,int amianownik)
                  {
                  set(alicznik,amianownik);
                  }
          	void set(int alicznik,int amianownik)
                  {
                  if (amianownik==0) throw "Ulamek: Blad #1";
                  licznik=alicznik;
                  mianownik=amianownik;
                  }
              void get(int& rlicznik,int& rmianownik) const
                  {
                  rlicznik=licznik;
                  rmianownik=mianownik;
                  }
              void get(int* plicznik,int* pmianownik) const
                  {
                  *plicznik=licznik;
                  *pmianownik=mianownik;
                  }
          };
  | Java
      package klasy;

      public class Ulamek
      {
        private int licznik,mianownik;

        public Ulamek(int alicznik,int amianownik)
          throws Exception
          {
          try
            {
            set(alicznik,amianownik);
            }
          catch(Exception E)
            {
            throw E;
            }
          }

        public void set(int alicznik,int amianownik)
          throws Exception
          {
          if (amianownik==0) throw new Exception("Ulamek: Blad #1");
          licznik=alicznik;
          mianownik=amianownik;
          }

        public void get(int[] arg)
          {
          arg[0]=licznik;
          arg[1]=mianownik;
          }

        public int[] get()
          {
          int[] wynik=new int[2];
          wynik[0]=licznik;
          wynik[1]=mianownik;
          return wynik;
          }
      }
  | Pascal
      unit _ulamek;

      interface

      uses SysUtils;

      type
      Ulamek = class
          private
              licznik :Integer;
              mianownik :Integer;
          public
              constructor Create(const alicznik, amianownik :Integer);
              procedure SetValues(const alicznik, amianownik :Integer);
              procedure GetValues(var rlicznik :Integer; var rmianownik :Integer); overload;
              procedure GetValues(plicznik, pmianownik :PInteger); overload;
          end;

      implementation

      constructor Ulamek.Create(const alicznik, amianownik :Integer);
      begin
      inherited Create; //TObject.Create()
      SetValues(alicznik,amianownik);
      end;

      procedure Ulamek.SetValues(const alicznik, amianownik :Integer);
      begin
      if amianownik=0 then raise Exception.Create('Ulamek: Blad #1');
      licznik:=alicznik;
      mianownik:=amianownik;
      end;

      procedure Ulamek.GetValues(var rlicznik :Integer; var rmianownik :Integer);
      begin
      rlicznik:=licznik;
      rmianownik:=mianownik;
      end;

      procedure Ulamek.GetValues(plicznik :PInteger; pmianownik :PInteger);
      begin
      plicznik^:=licznik;
      pmianownik^:=mianownik;
      end;

      end.
  | C#
      public class Ulamek
      {
      	private int licznik,mianownik;

      	public Ulamek(int alicznik,int amianownik)
      	{
      		set(alicznik,amianownik);
      	}

      	public void set(int alicznik,int amianownik)
      	{
      		if (amianownik==0) throw new Exception("Ulamek: Blad #1");
      		licznik=alicznik;
      		mianownik=amianownik;
      	}

      	public void get(out int rlicznik,out int rmianownik)
      	{
      		rlicznik=licznik;
      		rmianownik=mianownik;
      	}

      	public void get(int[] arg)
      	{
      		arg[0]=licznik;
      		arg[1]=mianownik;
      	}

      	public int[] get()
      	{
      		int[] wynik=new int[2];
      		wynik[0]=licznik;
      		wynik[1]=mianownik;
      		return wynik;
      	}
      }
  Przykład kodu:
      ulamek a(1,2);
      ulamek* b=new ulamek(3,4);

      delete b;
  | Przykład kodu:
      Ulamek a=null,b=null,c=null;
      try
        {
        a=new Ulamek(1,2);
        b=new Ulamek(3,4);
        c=new Ulamek(1,0);
        }
      catch(Exception E)
        {
        list1.add(E.getMessage());
        }

      int[] licznik_mianownik=new int[2];
      a.get(licznik_mianownik);
      list1.add("a: "+String.valueOf(licznik_mianownik[0])+"/"+
                      String.valueOf(licznik_mianownik[1]));
      b.get(licznik_mianownik);
      list1.add("b: "+String.valueOf(licznik_mianownik[0])+"/"+
                      String.valueOf(licznik_mianownik[1]));
      c.get(licznik_mianownik);
      list1.add("c: "+String.valueOf(licznik_mianownik[0])+"/"+
                      String.valueOf(licznik_mianownik[1]));
  | Przykład kodu:
      procedure TForm1.Button1Click(Sender: TObject);
      var a :Ulamek;
          licznik,mianownik :Integer;
      begin
      a:=Ulamek.Create(1,2);
      a.Free;
      end;
  | Przykład kodu:
      Ulamek a=new Ulamek(1,2);

      int licznik,mianownik;
      a.get(out licznik,out mianownik);
      MessageBox.Show("a: "+licznik+"/"+mianownik);

  Uwaga!

  - C++: Własnoręczne zdefiniowanie konstruktora powoduje, ze kompilator nie tworzy konstruktora bazowego.
  - C++: Nie mozna już zdefiniować zmiennej bez podania wartości inicjujących, ale tak jest "zdrowiej", bo wykluczona jest obecność obiektu bez nadanych przez nas wartościach pól.
  - C++: Można zachować możliwość deklarowania obiektu klasy ulamek bez podawania wartości inicjujących przez wykorzystanie wartości domyślnych w konstruktorze (silnie polecane): ulamek(int alicznik=0,int amianownik=1). Wówczas obiekt jest inicjowany domyślnymi wartościami.
  - C++: Jeszcze większą korzyścią jest uzyskanie tanim kosztem możliwośći konwersji z typu int. Wyrażenie ulamek(3) jest bowiem równoważne teraz wyrażeniu ulamek(3,1). Co więcej możliwe jest naprzykład napisanie ulamek a=3; czyli w istocie skorzystane z niejawnej konwersji typu int na ulamek: ulamek a=(ulamek)3;.
  - Delphi: Również w Delphi możliwe jest ustalanie domyślnych wartości argumentów metod i funkcji: constructor Create(alicznik :Integer = 0; amianownik :Integer = 1);.
  - Java: Java nie pozwala na ustalanie wartości domyślnych. W zastępstwie możemy zdefiniować konstruktor bezargumentowy wywołujący konstruktor inicjujący pola klasy:

          public Ulamek()
            throws Exception
            {
            this(0,1);
            }

  - Delphi: W Object Pascalu operatory konwersji nie istnieją, w zamian język oferuje funkcji konwertujące. Możemy zdefiniować własną procedurę lub funkcję pozwalają na konwersję z iczby całkowitej do ulamek:

        procedure IntToUlamek(const liczba :Integer; var RUlamek :Ulamek);
        begin
        if Assigned(RUlamek) then RUlamek.SetValues(liczba,1);
        end;

    lub alternatywnie

        function IntToUlamek(const liczba :Integer) :Ulamek;
        begin
        IntToUlamek:=Ulamek.Create(liczba,1);
        end;

  #### Własne klasy wyjatkow:

  Wyjątek - obiekt zawierający informację o sytuacji krytycznej, który jest przesyłany poleceniem throw po jej wystąpieniu. Wyjątek może być przechwycony jeżeli miejsce jego wystąpienia otoczone jest przez try {...} catch() {...}. Wówczas wykonywany jest kod znajdujący się w bloku następującym po catch, a następnie kod znajdujący się za nim (program nie wraca do poleceń znajdujących się za throw. W standardzie C++ znajduje się domyślny obiekt wyjątku std::exception (VCL posiada własny Exception), ale można też napisać własną klasę lub przesyłać, jak w poprzednim przykładzie dotyczącym wyjątków, np. tablicę znaków.

  C++
      class ulamek_exception
          {
          private:
          	char* message;
          public:
      		ulamek_exception(char* amessage) {message=amessage;};
      		char* what() const {return message;};
          };

      class ulamek
          {
          private:
          	int licznik, mianownik;
          public:
          	ulamek(int alicznik=0,int amianownik=1)
                  {
                  set(alicznik,amianownik);
                  }
          	void set(int alicznik,int amianownik)
                  {
                  if (amianownik==0) throw ulamek_exception("Ulamek: Blad #1");
                  licznik=alicznik;
                  mianownik=amianownik;
                  }
              void get(int& rlicznik,int& rmianownik) const
                  {
                  rlicznik=licznik;
                  rmianownik=mianownik;
                  }
              void get(int* plicznik,int* pmianownik) const
                  {
                  *plicznik=licznik;
                  *pmianownik=mianownik;
                  }
          };
  | Java
      package klasy;

      class Ulamek_Exception extends Exception
      {
        private String message;
        public Ulamek_Exception(String amessage) {message=amessage;}
        public String what() {return message;}
      }

      public class Ulamek
      {
        private int licznik,mianownik;

        public Ulamek(int alicznik,int amianownik)
          throws Ulamek_Exception
          {
          try
            {
            set(alicznik,amianownik);
            }
          catch(Ulamek_Exception E)
            {
            throw E;
            }
          }

        public void set(int alicznik,int amianownik)
          throws Ulamek_Exception
          {
          if (amianownik==0) throw new Ulamek_Exception("Ulamek: Blad #1");
          licznik=alicznik;
          mianownik=amianownik;
          }

        public void int[] arg)
          {
          arg[0]=licznik;
          arg[1]=mianownik;
          }

        public int[] get()
          {
          int[] wynik=new int[2];
          wynik[0]=licznik;
          wynik[1]=mianownik;
          return wynik;
          }
      }
  | Pascal
      unit _ulamek;

      interface

      uses SysUtils;

      type
      Ulamek_Exception = class(Exception)
          private
              Message :String;
          public
              constructor Create(const AMessage :String);
              function What :String;
          end;

      Ulamek = class
          private
              licznik :Integer;
              mianownik :Integer;
          public
              constructor Create(const alicznik :Integer =0; const amianownik :Integer =1);
              procedure SetValues(const alicznik, amianownik :Integer);
              procedure GetValues(var rlicznik :Integer; var rmianownik :Integer); overload;
              procedure GetValues(plicznik, pmianownik :PInteger); overload;
          end;

      function IntToUlamek(const liczba :Integer) :Ulamek;

      implementation

      constructor Ulamek_Exception.Create(const AMessage :String);
      begin
      Message:=AMessage;
      end;

      function Ulamek_Exception.What :String;
      begin
      What:=Message;
      end;

      constructor Ulamek.Create(const alicznik :Integer =0; const amianownik :Integer =1);
      begin
      inherited Create;
      SetValues(alicznik,amianownik);
      end;

      procedure Ulamek.SetValues(const alicznik, amianownik :Integer);
      begin
      if amianownik=0 then raise Ulamek_Exception.Create('Ulamek: Blad #1');
      licznik:=alicznik;
      mianownik:=amianownik;
      end;

      procedure Ulamek.GetValues(var rlicznik :Integer; var rmianownik :Integer);
      begin
      rlicznik:=licznik;
      rmianownik:=mianownik;
      end;

      procedure Ulamek.GetValues(plicznik :PInteger; pmianownik :PInteger);
      begin
      plicznik^:=licznik;
      pmianownik^:=mianownik;
      end;

      //FUNKCJA KONWERTUJACA
      function IntToUlamek(const liczba :Integer) :Ulamek;
      begin
      IntToUlamek:=Ulamek.Create(liczba,1);
      end;

      end.
  | C#
      class Ulamek_Exception : Exception
      {
      	private String message;
      	public Ulamek_Exception(String amessage) {message=amessage;}
      	public String what() {return message;}
      }

      public class Ulamek
      {
      	private int licznik,mianownik;

      	public Ulamek(int alicznik,int amianownik)
      		{
      		set(alicznik,amianownik);
      		}

      	public void set(int alicznik,int amianownik)
      		{
      		if (amianownik==0) throw new Ulamek_Exception("Ulamek: Blad #1");
      		licznik=alicznik;
      		mianownik=amianownik;
      		}

      	public void get(out int rlicznik,out int rmianownik)
      		{
      		rlicznik=licznik;
      		rmianownik=mianownik;
      		}

      	public void get(int[] arg)
      		{
      		arg[0]=licznik;
      		arg[1]=mianownik;
      		}

      	public int[] get()
      		{
      		int[] wynik=new int[2];
      		wynik[0]=licznik;
      		wynik[1]=mianownik;
      		return wynik;
      		}
      }
  Przykład kodu:
      ulamek* c=NULL;
      try
          {
          c=new ulamek(1,0);
          }
      catch(ulamek_exception& E)
          {
          ShowMessage(E.what());
          c=NULL;
          }

      if (c!=NULL)
          {
          c->get(licznik,mianownik);
          ShowMessage((AnsiString)"c: "+licznik+"/"+mianownik);
          delete c;
          }
  | Przykład kodu:
      Ulamek a=null,b=null,c=null;
      try
        {
        a=new Ulamek(1,2);
        b=new Ulamek(3,4);
        c=new Ulamek(1,0);
        }
      catch(Ulamek_Exception E)
        {
        list1.add(E.what());
        }

      int[] licznik_mianownik=new int[2];
      a.get(licznik_mianownik);
      list1.add("a: "+String.valueOf(licznik_mianownik[0])+"/"+
                      String.valueOf(licznik_mianownik[1]));
      b.get(licznik_mianownik);
      list1.add("b: "+String.valueOf(licznik_mianownik[0])+"/"+
                      String.valueOf(licznik_mianownik[1]));
      c.get(licznik_mianownik);
      list1.add("c: "+String.valueOf(licznik_mianownik[0])+"/"+
                      String.valueOf(licznik_mianownik[1]));
  | Przykład kodu:
      procedure TForm1.Button1Click(Sender: TObject);
      var c :Ulamek;
      begin
          try
              c:=Ulamek.Create(1,0);
          except
              on E :Ulamek_Exception do ShowMessage(E.What);
          end;
      end;
  | Przykład kodu:
      Ulamek a=null,b=null,c=null;
      try
      {
      	a=new Ulamek(1,2);
      	b=new Ulamek(3,4);
      	c=new Ulamek(1,0);
      }
      catch(Ulamek_Exception E)
      {
      	MessageBox.Show(E.what());
      }

      int licznik,mianownik;
      a.get(out licznik,out mianownik);
      MessageBox.Show("a: "+licznik+"/"+mianownik);

      b.get(out licznik,out mianownik);
      MessageBox.Show("b: "+licznik+"/"+mianownik);

      c.get(out licznik,out mianownik);
      MessageBox.Show("c: "+licznik+"/"+mianownik);

  Uwaga!
  Po przesłaniu wyjątku program nie wraca do dalszych linii następujących po throw.

  #### Konstruktor copy:

  1. Konstruktor kopiujący jest tworzony przez kompilator i poza rzadkimi wypadkami, gdy podczas kopiowania pól będących wskaźnikami chcemy tworzyć odrębną instancję wskazywanego przez ten adres obiektu, nie ma najmniejszego uzasadnienia do jego własnoręcznego pisania.
  2. Kontruktor kopiujący jest wykorzystywany podczas inicjowania obiektu, a więc w naszym przypadku może to być: ulamek b=a (a jest obiektem klasy ulamek) lub przy tworzeniu instancji obiektu przy przesyłaniu go jako argument funkcji.
  3. Nie nalezy mylic sytuacji, w której wykorzystywany jest konstruktor copy ze zwyklym wykorzystaniem operatora przypisania =, np. b=a (przy wcześniejszym zadeklarowaniu i zainicjowaniu obiektów a i b np. jako ulamek a(1,2), b(3,4);).

  W Javie zastąpiłem przykład konstruktora kopiującego, który w tym języku nie występuje, przykładem wykorzystania metody clone(), który przejmuje niektóre jego funkcje. Podobnie w C#, w której zdefiniowana została metoda Clone().

  C++
      class ulamek
          {
          private:
          	int licznik, mianownik;
          public:
          	ulamek(int alicznik=0,int amianownik=1)
                  {
                  set(alicznik,amianownik);
                  }
          	ulamek(const ulamek& aulamek)
                  {
                  aulamek.get(licznik,mianownik);
                  }
          	void set(int alicznik,int amianownik)
                  {
                  if (amianownik==0) throw "Ulamek: Blad #1";
                  licznik=alicznik;
                  mianownik=amianownik;
                  }
              void get(int& rlicznik,int& rmianownik) const
                  {
                  rlicznik=licznik;
                  rmianownik=mianownik;
                  }
              void get(int* plicznik,int* pmianownik) const
                  {
                  *plicznik=licznik;
                  *pmianownik=mianownik;
                  }
          };
  | Java
      package klasy;

      public class Ulamek implements Cloneable
      {
        private int licznik,mianownik;

        public Ulamek(int alicznik,int amianownik)
          throws Exception
          {
          try
            {
            set(alicznik,amianownik);
            }
          catch(Exception E)
            {
            throw E;
            }
          }

        public Object clone()
          throws CloneNotSupportedException
          {
          Object wynik=null;
          try
            {
            wynik=super.clone();
            }
          catch(CloneNotSupportedException E)
            {
            throw E;
            }
          return wynik;
          }

        public void set(int alicznik,int amianownik)
          throws Exception
          {
          if (amianownik==0) throw new Exception("Ulamek: Blad #1");
          licznik=alicznik;
          mianownik=amianownik;
          }

        public void get(int[] arg)
          {
          arg[0]=licznik;
          arg[1]=mianownik;
          }

        public int[] get()
          {
          int[] wynik=new int[2];
          wynik[0]=licznik;
          wynik[1]=mianownik;
          return wynik;
          }
      }
  | Pascal
  | C#
      public class Ulamek
      {
      	private int licznik,mianownik;

      	public Ulamek(int alicznik,int amianownik)
      		{
      		set(alicznik,amianownik);
      		}

      	//cos podobnego do konstruktora copy w C++
      	public Ulamek(Ulamek aulamek)
      		{
      		int olicznik,omianownik;
      		aulamek.get(out olicznik,out omianownik);
      		set(olicznik,omianownik);
      		}

      	//analog funkcji clone() w Javie
      	public Ulamek Clone()
      		{
      		return new Ulamek(licznik,mianownik);
      		}

      	public void set(int alicznik,int amianownik)
      		{
      		if (amianownik==0) throw new Ulamek_Exception("Ulamek: Blad #1");
      		licznik=alicznik;
      		mianownik=amianownik;
      		}

      	public void get(out int rlicznik,out int rmianownik)
      		{
      		rlicznik=licznik;
      		rmianownik=mianownik;
      		}

      	public void get(int[] arg)
      		{
      		arg[0]=licznik;
      		arg[1]=mianownik;
      		}

      	public int[] get()
      		{
      		int[] wynik=new int[2];
      		wynik[0]=licznik;
      		wynik[1]=mianownik;
      		return wynik;
      		}
      }
  Przykład kodu
      ulamek a(1,2); //konstruktor
      ulamek c(a); //konstruktor copy
      ulamek b=a; //konstruktor copy
      a.set(3,4); //zmiana stanu pierwotnego obiektu
      c=b; //operator przypisania =
  | Przykład kodu:
      Ulamek a=null,b=null,c=null;
      try
        {
        a=new Ulamek(1,2);
        b=new Ulamek(3,4);
        c=(Ulamek)a.clone();
        a.set(3,4);
        }
      catch(CloneNotSupportedException CloneE)
        {
        list1.add(CloneE.getMessage());
        }
      catch(Exception E)
        {
        list1.add(E.getMessage());
        }

      int[] licznik_mianownik=new int[2];
      a.get(licznik_mianownik);
      list1.add("a: "+String.valueOf(licznik_mianownik[0])+"/"+
                      String.valueOf(licznik_mianownik[1]));
      b.get(licznik_mianownik);
      list1.add("b: "+String.valueOf(licznik_mianownik[0])+"/"+
                      String.valueOf(licznik_mianownik[1]));
      c.get(licznik_mianownik);
      list1.add("c: "+String.valueOf(licznik_mianownik[0])+"/"+
                      String.valueOf(licznik_mianownik[1]));
  | Pascal
  | Przykład kodu:
      Ulamek a=null,b=null,c=null;

      try
      {
      	a=new Ulamek(1,2);
      	c=new Ulamek(a);

      	b=a;
      	a.set(3,4);
      	//c=b;
      }
      catch(Ulamek_Exception E)
      {
      	MessageBox.Show(E.what());
      }

      int licznik,mianownik;
      a.get(out licznik,out mianownik);
      MessageBox.Show("a: "+licznik+"/"+mianownik);

      b.get(out licznik,out mianownik);
      MessageBox.Show("b: "+licznik+"/"+mianownik);

      c.get(out licznik,out mianownik);
      MessageBox.Show("c: "+licznik+"/"+mianownik);

  Uwaga!

  - C++: Kompilator tworzy sam konstruktor domyślny (jeżeli nie ma zadeklarowanego żadnego konstruktora), konstruktor copy oraz operator przypisania. Wszystkie mogą być przedefiniowane przez użytkownika.
  - C++, Java: czestym bledem przy obiektach tworzonych dynamicznie jest mylenie operatora przypisania na wskaznikach/zmiennych obiektowych z operatorem przypisania dotyczacym obiektow.

        Ulamek* a=new Ulamek(1,2);
        Ulamek* b=new Ulamek(3,4);
        b=a;
        a.set(0,1);
        //odczytanie b pokaze 0/1

    W tym przypadku wskaźniki a i b wskazują na ten sam obszar w pamięci zajmowany przez obiekt poczatkowo przypisany tylko do wskażnika a. Wszelkie zmiany na *a będą widoczne w *b i odwrotnie.
  - C#: Nie ma mozliwosci niejawnego wykorzystania "konstruktora copy".

  #### Obiekty stałe:

  C++
      const ulamek ulamek_zero(0,1);
      const ulamek ulamek_jeden(1,1);
      const ulamek ulamek_polowa(1,2);
      const ulamek ulamek_cwierc(1,4);
  Przykład kodu
      ulamek a=ulamek_polowa;
      ulamek b(ulamek_cwierc);

  Uwaga!

  - C++: Podobny efekt można uzyskać korzystając z dyrektywy #define, ale nie jest to rozwiązanie godne polecenia:

        #define ulamek_zero ulamek(0,1)
        #define ulamek_jeden ulamek(1,1)
        #define ulamek_polowa ulamek(1,2)
        #define ulamek_cwierc ulamek(1,4)

  - Delphi: W Object Pascalu stałe mogą być tylko typy wbudowane, ale można stworzyć serię alternatywnych konstruktorów np.:

        constructor Ulamek.Polowa;
        begin
        SetValues(1,2);
        end;

  - Java: W Javie również nie ma możliwości deklarowania obiektów stałych, ale w zamian można definiować obiekty danej klasy w obrębie tej klasy (np. Color.white). W naszym przypadku jest to niemożliwe ze względu na wymóg obsłużenia wyjątku, ale jeżeli gotowi jesteśmy zrezygnować z obsługi wyjątków możemy klasę zadeklarować następująco:

        public class Ulamek implements Cloneable
        {
          private int licznik,mianownik;
          public boolean error=false;

          public static final Ulamek Zero=new Ulamek(0,1);
          public static final Ulamek Jeden=new Ulamek(1,1);
          public static final Ulamek Polowa=new Ulamek(1,2);
          public static final Ulamek Cwierc=new Ulamek(1,4);

          public Ulamek(int alicznik,int amianownik)
            //throws Exception
            {
            try
              {
              set(alicznik,amianownik);
              }
            catch(Exception E)
              {
              //throw E;
              error=true;
              }
            }

        //reszta klasy

    Uwaga! Należy jednak być ostrożnym, bo nic nie przeszkadza wykorzystać metody set na takim statycznym polu, żeby zmienić jego stan!
  - C#: Można postąpić bardzo podobnie jak w Javie nawet bez konieczności rezygnacji z posługiwania się wyjątkami (z analogicznym ostrzeżeniem co do metody set), należy tylko zamienić słowo kluczowe final na readonly.

  #### Działania określone dla klasy, operatory:

  Działania na ułamkach (ew. na ułamkach i liczbach całkowitych) powinny zwracać ułamek. Należy więc zdefiniować odpowiednie operatory implementujące odpowiednie działania arytmetyczne: +(u,u), -(u), -(u,u), *(u,u), /(u,u), a ponadto <(u,u) oraz >(u,u). Ostatnie dwa zwracają wartość logiczną.

  Istnieją dwie możliwości tworzenia operatorów: pierwsza to operator będący elementem klasy (daje to pewne korzyści przy ewentualnym dziedziczeniu), druga to niezależny operator zdefiniowany dla jednego lub dwóch argumentów odpowiedniej klasy. Korzyścią drugiego sposobu jest rozciągnięcie niejawnej konwersji również na pierwszy argument, który w pierwszym przypadku musi być jawnie typu odpowiednij klasy.

  W Pascalu i Javie o definiowaniu operatorów nie ma co śnić (jedyny przeciążony operator to + dla klasy String) - wzamian zdefiniujemy funkcje. W Javie funkcje mogą być wyłącznie metodami, tj. muszą być związane z jakąś klasą, ale mogą być metodami statycznymi tj. nie wymagającymi do ich uruchomienia stworzenia obiektu.

  C++
      ulamek operator *(const ulamek& aulamek1,const ulamek& aulamek2)
          {
          int alicznik1=0,amianownik1=1;aulamek1.get(alicznik1,amianownik1);
          int alicznik2=0,amianownik2=1;aulamek2.get(alicznik2,amianownik2);
          return ulamek(alicznik1*alicznik2,amianownik1*amianownik2);
          };
      ulamek operator /(const ulamek& aulamek1,const ulamek& aulamek2)
          {
          int alicznik1=0,amianownik1=1;aulamek1.get(alicznik1,amianownik1);
          int alicznik2=0,amianownik2=1;aulamek2.get(alicznik2,amianownik2);
          return ulamek(alicznik1*amianownik2,amianownik1*alicznik2);
          };
      ulamek operator +(const ulamek& aulamek1,const ulamek& aulamek2)
          {
          int alicznik1=0,amianownik1=1;aulamek1.get(alicznik1,amianownik1);
          int alicznik2=0,amianownik2=1;aulamek2.get(alicznik2,amianownik2);
          return ulamek(alicznik1*amianownik2+alicznik2*amianownik1,amianownik1*amianownik2);
          };
      ulamek operator -(const ulamek& aulamek1)
          {
          int alicznik1=0,amianownik1=1;aulamek1.get(alicznik1,amianownik1);
          return ulamek(-alicznik1,amianownik1);
          };
      ulamek operator -(const ulamek& aulamek1,const ulamek& aulamek2)
          {
          int alicznik1=0,amianownik1=1;aulamek1.get(alicznik1,amianownik1);
          int alicznik2=0,amianownik2=1;aulamek2.get(alicznik2,amianownik2);
          return ulamek(alicznik1*amianownik2-alicznik2*amianownik1,amianownik1*amianownik2);
          };
      bool operator <(const ulamek& aulamek1,const ulamek& aulamek2)
          {
          int alicznik1=0,amianownik1=1;aulamek1.get(alicznik1,amianownik1);
          int alicznik2=0,amianownik2=1;aulamek2.get(alicznik2,amianownik2);
          return (alicznik1/(double)amianownik1)<(alicznik2/(double)amianownik2);
          };
      bool operator >(const ulamek& aulamek1,const ulamek& aulamek2)
          {
          int alicznik1=0,amianownik1=1;aulamek1.get(alicznik1,amianownik1);
          int alicznik2=0,amianownik2=1;aulamek2.get(alicznik2,amianownik2);
          return (alicznik1/(double)amianownik1)>(alicznik2/(double)amianownik2);
          };
      bool operator ==(const ulamek& aulamek1,const ulamek& aulamek2)
          {
          int alicznik1=0,amianownik1=1;aulamek1.get(alicznik1,amianownik1);
          int alicznik2=0,amianownik2=1;aulamek2.get(alicznik2,amianownik2);
          return (alicznik1/(double)amianownik1)==(alicznik2/(double)amianownik2);
          };
  | Java
      public static Ulamek Prod(Ulamek aulamek1,Ulamek aulamek2)
        throws Exception
        {
        int[] alicznik1_amianownik1=aulamek1.get();
        int[] alicznik2_amianownik2=aulamek2.get();
        Ulamek wynik=null;
        try
          {
          wynik=new Ulamek(alicznik1_amianownik1[0]*alicznik2_amianownik2[0],
      	                 alicznik1_amianownik1[1]*alicznik2_amianownik2[1]);
          }
        catch(Exception E) {throw E;}
        return wynik;
        }

      public static Ulamek Quot(Ulamek aulamek1,Ulamek aulamek2)
        throws Exception
        {
        int[] alicznik1_amianownik1=aulamek1.get();
        int[] alicznik2_amianownik2=aulamek2.get();
        Ulamek wynik=null;
        try
          {
          wynik=new Ulamek(alicznik1_amianownik1[0]*alicznik2_amianownik2[1],
      	                 alicznik1_amianownik1[1]*alicznik2_amianownik2[0]);
          }
        catch(Exception E) {throw E;}
        return wynik;
        }

      public static Ulamek Sum(Ulamek aulamek1,Ulamek aulamek2)
        throws Exception
        {
        int[] alicznik1_amianownik1=aulamek1.get();
        int[] alicznik2_amianownik2=aulamek2.get();
        Ulamek wynik=null;
        try
          {
          wynik=new Ulamek(alicznik1_amianownik1[0]*alicznik2_amianownik2[1]
      	                 +alicznik1_amianownik1[1]*alicznik2_amianownik2[0],
      	                 alicznik1_amianownik1[1]*alicznik2_amianownik2[1]);
          }
        catch(Exception E) {throw E;}
        return wynik;
        }

      public static Ulamek Revr(Ulamek aulamek1)
        throws Exception
        {
        int[] alicznik1_amianownik1=aulamek1.get();
        Ulamek wynik=null;
        try
          {
          wynik=new Ulamek(-alicznik1_amianownik1[0],alicznik1_amianownik1[1]);
          }
        catch(Exception E) {throw E;}
        return wynik;
        }

      public static Ulamek Diff(Ulamek aulamek1,Ulamek aulamek2)
        throws Exception
        {
        int[] alicznik1_amianownik1=aulamek1.get();
        int[] alicznik2_amianownik2=aulamek2.get();
        Ulamek wynik=null;
        try
          {
          wynik=new Ulamek(alicznik1_amianownik1[0]*alicznik2_amianownik2[1]
      	                 -alicznik1_amianownik1[1]*alicznik2_amianownik2[0],
      	                 alicznik1_amianownik1[1]*alicznik2_amianownik2[1]);
          }
        catch(Exception E) {throw E;}
        return wynik;
        }

      /* porownanie <, >, = */
      public static int Cmp(Ulamek aulamek1,Ulamek aulamek2)
        {
        int[] alicznik1_amianownik1=aulamek1.get();
        int[] alicznik2_amianownik2=aulamek2.get();
        double wynik=(alicznik1_amianownik1[0]/(double)alicznik1_amianownik1[1])
                     -(alicznik2_amianownik2[0]/(double)alicznik2_amianownik2[1]);
        byte int_wynik;
        if (wynik==0) int_wynik=(byte)(wynik); else int_wynik=(byte)(wynik/Math.abs(wynik));
        return int_wynik;
        }
  | Pascal
      {iloczyn}
      function UlamekProd(const AUlamek1, AUlamek2 :Ulamek) :Ulamek;
      var alicznik1,amianownik1,alicznik2,amianownik2 :Integer;
      begin
      AUlamek1.GetValues(alicznik1,amianownik1);
      AUlamek2.GetValues(alicznik2,amianownik2);
      UlamekProd:=Ulamek.Create(alicznik1*alicznik2,amianownik1*amianownik2);
      end;

      {iloraz}
      function UlamekQuot(const AUlamek1, AUlamek2 :Ulamek) :Ulamek;
      var alicznik1,amianownik1,alicznik2,amianownik2 :Integer;
      begin
      AUlamek1.GetValues(alicznik1,amianownik1);
      AUlamek2.GetValues(alicznik2,amianownik2);
      UlamekQuot:=Ulamek.Create(alicznik1*amianownik2,amianownik1*alicznik2);
      end;

      {suma}
      function UlamekSum(const AUlamek1, AUlamek2 :Ulamek) :Ulamek;
      var alicznik1,amianownik1,alicznik2,amianownik2 :Integer;
      begin
      AUlamek1.GetValues(alicznik1,amianownik1);
      AUlamek2.GetValues(alicznik2,amianownik2);
      UlamekSum:=Ulamek.Create(alicznik1*amianownik2+alicznik2*amianownik1,amianownik1*amianownik2);
      end;

      {zmiana znaku}
      function UlamekRevr(const AUlamek1 :Ulamek) :Ulamek;
      var alicznik1,amianownik1 :Integer;
      begin
      AUlamek1.GetValues(alicznik1,amianownik1);
      UlamekRevr:=Ulamek.Create(-alicznik1,amianownik1);
      end;

      {roznica}
      function UlamekDiff(const AUlamek1, AUlamek2 :Ulamek) :Ulamek;
      var alicznik1,amianownik1,alicznik2,amianownik2 :Integer;
      begin
      AUlamek1.GetValues(alicznik1,amianownik1);
      AUlamek2.GetValues(alicznik2,amianownik2);
      UlamekDiff:=Ulamek.Create(alicznik1*amianownik2-alicznik2*amianownik1,amianownik1*amianownik2);
      end;

      {porownanie <, >, =}
      function UlamekCmp(const AUlamek1, AUlamek2 :Ulamek) :Integer;
      var alicznik1,amianownik1,alicznik2,amianownik2 :Integer;
          wynik :Double;
          int_wynik :Integer;
      begin
      AUlamek1.GetValues(alicznik1,amianownik1);
      AUlamek2.GetValues(alicznik2,amianownik2);
      wynik:=(alicznik1/amianownik1)-(alicznik1/amianownik2);
      if wynik=0 then int_wynik:=Round(wynik) else int_wynik:=Round(wynik/abs(wynik));
      UlamekCmp:=int_wynik;
      end;
  | C#
      public static Ulamek operator *(Ulamek aulamek1,Ulamek aulamek2)
      	{
      	int alicznik1,amianownik1;aulamek1.get(out alicznik1,out amianownik1);
      	int alicznik2,amianownik2;aulamek2.get(out alicznik2,out amianownik2);
      	return new Ulamek(alicznik1*alicznik2,amianownik1*amianownik2);
      	}

      public static Ulamek operator /(Ulamek aulamek1,Ulamek aulamek2)
      	{
      	int alicznik1,amianownik1;aulamek1.get(out alicznik1,out amianownik1);
      	int alicznik2,amianownik2;aulamek2.get(out alicznik2,out amianownik2);
      	return new Ulamek(alicznik1*amianownik2,amianownik1*alicznik2);
      	}

      public static Ulamek operator +(Ulamek aulamek1,Ulamek aulamek2)
      	{
      	int alicznik1,amianownik1;aulamek1.get(out alicznik1,out amianownik1);
      	int alicznik2,amianownik2;aulamek2.get(out alicznik2,out amianownik2);
      	return new Ulamek(alicznik1*amianownik2+alicznik2*amianownik1,amianownik1*amianownik2);
      	}

      public static Ulamek operator -(Ulamek aulamek1)
      	{
      	int alicznik1,amianownik1;aulamek1.get(out alicznik1,out amianownik1);
      	return new Ulamek(-alicznik1,amianownik1);
      	}

      public static Ulamek operator -(Ulamek aulamek1,Ulamek aulamek2)
      	{
      	int alicznik1,amianownik1;aulamek1.get(out alicznik1,out amianownik1);
      	int alicznik2,amianownik2;aulamek2.get(out alicznik2,out amianownik2);
      	return new Ulamek(alicznik1*amianownik2-alicznik2*amianownik1,amianownik1*amianownik2);
      	}

      public static bool operator <(Ulamek aulamek1,Ulamek aulamek2)
      	{
      	int alicznik1,amianownik1;aulamek1.get(out alicznik1,out amianownik1);
      	int alicznik2,amianownik2;aulamek2.get(out alicznik2,out amianownik2);
      	return (alicznik1/(double)amianownik1)<(alicznik2/(double)amianownik2);
      	}

      public static bool operator >(Ulamek aulamek1,Ulamek aulamek2)
      	{
      	int alicznik1,amianownik1;aulamek1.get(out alicznik1,out amianownik1);
      	int alicznik2,amianownik2;aulamek2.get(out alicznik2,out amianownik2);
      	return (alicznik1/(double)amianownik1)>(alicznik2/(double)amianownik2);
      	}

      public override bool Equals(object obj)
      	{
      	if (obj==null) return false;
      	Ulamek aulamek2=(Ulamek)obj;
      	int alicznik2,amianownik2;aulamek2.get(out alicznik2,out amianownik2);
      	return (licznik/(double)mianownik)==(alicznik2/(double)amianownik2);
      	}

      public override int GetHashCode()
      	{
      	return licznik^mianownik;
      	}

      public static bool operator ==(Ulamek aulamek1,Ulamek aulamek2)
      	{
      	return aulamek1.Equals((Object)aulamek2);
      	}

      public static bool operator !=(Ulamek aulamek1,Ulamek aulamek2)
      	{
      	return !(aulamek1.Equals((Object)aulamek2));
      	}
  Przykład kodu
      ulamek a=ulamek_polowa;
      ulamek b(ulamek_cwierc);

      ulamek c=a*b;
      ulamek d(2*c);

      c=a/b/2;
      d=2/a;

      c=a+b-2;
      d=2-(-a);

      try {c=a/ulamek_zero;} catch(char* E) {ShowMessage(E);}
      try {d=a/0;} catch(char* E) {ShowMessage(E);}

      if (a<b) ShowMessage("a < b"); else ShowMessage("a >= b");
      if (a<1) ShowMessage("a < 1"); else ShowMessage("a >= 1");
      if (0<a) ShowMessage("a > 0"); else ShowMessage("a <= 0");

      if (b>a) ShowMessage("a < b"); else ShowMessage("a >= b");
      if (1>a) ShowMessage("a < 1"); else ShowMessage("a >= 1");
      if (a>0) ShowMessage("a > 0"); else ShowMessage("a <= 0");

      if (a==b) ShowMessage("a = b"); else ShowMessage("a != b");
      if (a==1) ShowMessage("a = 1"); else ShowMessage("a != 1");
      if (a==0) ShowMessage("a = 0"); else ShowMessage("a != 0");
  | Przykład kodu:
      Ulamek a=null,b=null,c=null,d=null;
      try
        {
        a=new Ulamek(1,2);
        b=new Ulamek(3,4);

        c=Ulamek.Prod(a,b);
        d=Ulamek.Prod(a,new Ulamek(2,1));

        c=Ulamek.Quot(a,b);
        d=Ulamek.Quot(new Ulamek(2,1),a);

        c=Ulamek.Sum(Ulamek.Sum(a,b),new Ulamek(2,1));
        d=Ulamek.Diff(new Ulamek(2,1),Ulamek.Revr(a));
        }
      catch(Exception E)
        {
        list1.add(E.getMessage());
        }

      int[] licznik_mianownik=new int[2];
      a.get(licznik_mianownik);
      list1.add("a: "+String.valueOf(licznik_mianownik[0])+"/"+
                      String.valueOf(licznik_mianownik[1]));
      b.get(licznik_mianownik);
      list1.add("b: "+String.valueOf(licznik_mianownik[0])+"/"+
                      String.valueOf(licznik_mianownik[1]));
      c.get(licznik_mianownik);
      list1.add("c: "+String.valueOf(licznik_mianownik[0])+"/"+
                      String.valueOf(licznik_mianownik[1]));
      d.get(licznik_mianownik);
      list1.add("d: "+String.valueOf(licznik_mianownik[0])+"/"+
                      String.valueOf(licznik_mianownik[1]));

      switch (Ulamek.Cmp(a,b))
        {
        case -1: list1.add("a < b"); break;
        case  0: list1.add("a = b"); break;
        case  1: list1.add("a > b"); break;
        default: list1.add("Blad");
        }
  | Pascal
      procedure TForm1.Button1Click(Sender: TObject);
      var a,b,c,d :Ulamek;
          licznik,mianownik :Integer;
      begin
      a:=Ulamek.Create(1,2);
      b:=Ulamek.Create(1,4);

      c:=UlamekProd(a,b);
      d:=UlamekProd(IntToUlamek(2),c);

      c:=UlamekQuot(UlamekQuot(a,b),IntToUlamek(2));
      d:=UlamekQuot(IntToUlamek(2),c);

      c:=UlamekDiff(UlamekSum(a,b),IntToUlamek(2));
      d:=UlamekDiff(IntToUlamek(2),UlamekRevr(a));

      try
        c:=UlamekQuot(a,IntToUlamek(0));
      except
        on E: Exception do ShowMessage(E.Message);
      end;

      c.GetValues(licznik,mianownik);
      ShowMessage('c: '+IntToStr(licznik)+'/'+IntToStr(mianownik));
      d.GetValues(licznik,mianownik);
      ShowMessage('d: '+IntToStr(licznik)+'/'+IntToStr(mianownik));

      case UlamekCmp(a,b) of
        -1: ShowMessage('a < b');
         0: ShowMessage('a = b');
         1: ShowMessage('a > b');
      else
         ShowMessage('Blad');
      end;
      end;
  | Przykład kodu:
      Ulamek a=Ulamek.Polowa;
      Ulamek b=new Ulamek(Ulamek.Cwierc);
      Ulamek c=a*b;

      int licznik,mianownik;
      c.get(out licznik,out mianownik);
      MessageBox.Show("c: "+licznik+"/"+mianownik);
      MessageBox.Show(a=b");
      MessageBox.Show(a==b?"a=b":"a!=b");

  Uwaga!

  - C++: Nie ma potrzeby definiowania operatora przypisania =, który tworzony jest automatycznie.
  - C++: Ponieważ konstruktor zapewnia konstrukcję z int do ulamek, operatory działają również gdy jako jeden argument wykorzystam liczbę całkowitą, np. int.
  - C++, Delphi: Próba podzielenia przez zero skończy się wywołaniem wyjątku w metodzie set/SetValues.
  - C#: Operatory można zdefiniować jedynie wewnątrz klasy (zresztą podobnie jak wszystkie inne elementy poza klasami).
  - C#: Nie można zdefiniować operatora < nie definiując operatora >. Analogicznie operatory == i !=.
  - C#: Na wzór Javy można zrezygnować z własnej definicji operatorow == i !=, pozostawiając je do porówywnania wskażników.
  - C#: Na implementację metody Equals narzucony jest warunek spełniania kilku relacji, m.in.: zwrotność, symetria, przyrównanie do null daje false i inne.
  - C#: Do definicji metody GetHashCode korzystam z operaotora XOR ^.

  #### Operatory/funkcje konwersji/rzutowania (ang. typecasting):

  C++: Korzystając z możliwości domyślnego ustalania wartości w konstruktorze dysponujemy konwersją typu int na typ ulamek. Dzięki temu nie musimy przeciążać operatorów dla liczb naturalnych i ułamków. Teraz warto zdefiniować operator konwersji z ulamek na double (konwersja na int nie ma oczywiście sensu, wiąże się bowiem z utratą informacji, ale na upartego można będzie przekonwertować ułamek na double, a potem double na int). W tym celu należy zdefiniować odpowiedni operator konwersji. Przydatna wydaje się również konwersja na łańuch znaków.

  Delphi, Java: W Object Pascalu i Javie zasady konwersji dla typów wbudowanych są ściśle określone (np. możliwe jest int->double, ale nie odwrotne). Wiele konwersji musi być jawna poprzez funkcje (np. round() dla zmiany double na int). Można rzutować także obiekty na jego typ bazowy (zawężanie) i odwrotnie (rozszerzanie). Tylko to drugie jest zawsze bezpieczne. Obiekty mogą oczywiście równiez posiadać zdefinowane przez programistę metody pozwalające na dokonanie konwersji.

  C++
      class ulamek
          {
          private:
          	int licznik, mianownik;
              double to_double()
                  {
                  return licznik/(double)mianownik;
                  }
              char* to_string()
                  {
                  char str_licznik[ulamek_maxstr]; itoa(licznik,str_licznik,10);
                  char str_mianownik[ulamek_maxstr]; itoa(mianownik,str_mianownik,10);
                  char* str=str_licznik;
                  strcat(str,"/");
                  strcat(str,str_mianownik);
                  return str;
                  }
          public:
          	ulamek(int alicznik=0,int amianownik=1)
                  {
                  set(alicznik,amianownik);
                  }
          	ulamek(const ulamek& aulamek)
                  {
                  aulamek.get(licznik,mianownik);
                  }
          	void set(int alicznik,int amianownik)
                  {
                  if (amianownik==0) throw "Ulamek: Blad #1";
                  licznik=alicznik;
                  mianownik=amianownik;
                  }
              void get(int& rlicznik,int& rmianownik) const
                  {
                  rlicznik=licznik;
                  rmianownik=mianownik;
                  }
              void get(int* plicznik,int* pmianownik) const
                  {
                  *plicznik=licznik;
                  *pmianownik=mianownik;
                  }
              operator double() const
                  {
                  return to_double();
                  }
              operator char*() const
                  {
                  return to_string();
                  }
          };
  | Java
      package klasy;

      public class Ulamek implements Cloneable
      {
        private int licznik,mianownik;

        public Ulamek(int alicznik,int amianownik)
          throws Exception
          {
          try
            {
            set(alicznik,amianownik);
            }
          catch(Exception E)
            {
            throw E;
            }
          }

        public Object clone()
          throws CloneNotSupportedException
          {
          Object wynik=null;
          try
            {
            wynik=super.clone();
            }
          catch(CloneNotSupportedException E)
            {
            throw E;
            }
          return wynik;
          }

        public void set(int alicznik,int amianownik)
          throws Exception
          {
          if (amianownik==0) throw new Exception("Ulamek: Blad #1");
          licznik=alicznik;
          mianownik=amianownik;
          }

        public void get(int[] arg)
          {
          arg[0]=licznik;
          arg[1]=mianownik;
          }

        public int[] get()
          {
          int[] wynik=new int[2];
          wynik[0]=licznik;
          wynik[1]=mianownik;
          return wynik;
          }

        /* POMINIĘTE FUNKCJE DZIAŁAŃ ARYTMETYCZNYCH I PORÓWNANIA WARTOŚCI */

        public double toDouble()
          {
          return licznik/(double)mianownik;
          }

        //nadpisana na Object.toString()
        public String toString()
          {
          return String.valueOf(licznik)+"/"+String.valueOf(mianownik);
          }

      }
  | Pascal
      unit _ulamek;

      interface

      uses SysUtils;

      type
      Ulamek_Exception = class(Exception)
          private
              Message :String;
          public
              constructor Create(const AMessage :String);
              function What :String;
          end;

      Ulamek = class
          private
              licznik :Integer;
              mianownik :Integer;
          public
              constructor Create(const alicznik :Integer =0; const amianownik :Integer =1);
              procedure SetValues(const alicznik, amianownik :Integer);
              procedure GetValues(var rlicznik :Integer; var rmianownik :Integer); overload;
              procedure GetValues(plicznik, pmianownik :PInteger); overload;
              function ToFloat :Extended;
              function ToString :String;
          end;

      function IntToUlamek(const liczba :Integer) :Ulamek;
      function UlamekProd(const AUlamek1, AUlamek2 :Ulamek) :Ulamek;
      function UlamekQuot(const AUlamek1, AUlamek2 :Ulamek) :Ulamek;
      function UlamekSum(const AUlamek1, AUlamek2 :Ulamek) :Ulamek;
      function UlamekRevr(const AUlamek1 :Ulamek) :Ulamek;
      function UlamekDiff(const AUlamek1, AUlamek2 :Ulamek) :Ulamek;
      function UlamekCmp(const AUlamek1, AUlamek2 :Ulamek) :Integer;
      function UlamekToFloat(const AUlamek :Ulamek) :Extended;
      function UlamekToStr(const AUlamek :Ulamek) :String;

      implementation

      constructor Ulamek_Exception.Create(const AMessage :String);
      begin
      Message:=AMessage;
      end;

      function Ulamek_Exception.What :String;
      begin
      What:=Message;
      end;

      constructor Ulamek.Create(const alicznik :Integer =0; const amianownik :Integer =1);
      begin
      inherited Create; //TObject.Create()
      SetValues(alicznik,amianownik);
      end;

      procedure Ulamek.SetValues(const alicznik, amianownik :Integer);
      begin
      if amianownik=0 then raise Ulamek_Exception.Create('Ulamek: Blad #1');
      licznik:=alicznik;
      mianownik:=amianownik;
      end;

      procedure Ulamek.GetValues(var rlicznik :Integer; var rmianownik :Integer);
      begin
      rlicznik:=licznik;
      rmianownik:=mianownik;
      end;

      procedure Ulamek.GetValues(plicznik :PInteger; pmianownik :PInteger);
      begin
      plicznik^:=licznik;
      pmianownik^:=mianownik;
      end;

      function Ulamek.ToFloat :Extended;
      begin
      ToFloat:=licznik/mianownik;
      end;

      function Ulamek.ToString :String;
      begin
      ToString:=IntToStr(licznik)+'/'+IntToStr(mianownik);
      end;

      { FUNKCJA KONWERTUJACA }

      function IntToUlamek(const liczba :Integer) :Ulamek;
      begin
      IntToUlamek:=Ulamek.Create(liczba,1);
      end;

      function UlamekToFloat(const AUlamek :Ulamek) :Extended;
      begin
      UlamekToFloat:=AUlamek.ToFloat;
      end;

      function UlamekToStr(const AUlamek :Ulamek) :String;
      begin
      UlamekToStr:=AUlamek.ToString;
      end;

      {OPERACJE NA ULAMKACH }
      {iloczyn}
      function UlamekProd(const AUlamek1, AUlamek2 :Ulamek) :Ulamek;
      var alicznik1,amianownik1,alicznik2,amianownik2 :Integer;
      begin
      AUlamek1.GetValues(alicznik1,amianownik1);
      AUlamek2.GetValues(alicznik2,amianownik2);
      UlamekProd:=Ulamek.Create(alicznik1*alicznik2,amianownik1*amianownik2);
      end;

      {iloraz}
      function UlamekQuot(const AUlamek1, AUlamek2 :Ulamek) :Ulamek;
      var alicznik1,amianownik1,alicznik2,amianownik2 :Integer;
      begin
      AUlamek1.GetValues(alicznik1,amianownik1);
      AUlamek2.GetValues(alicznik2,amianownik2);
      UlamekQuot:=Ulamek.Create(alicznik1*amianownik2,amianownik1*alicznik2);
      end;

      {suma}
      function UlamekSum(const AUlamek1, AUlamek2 :Ulamek) :Ulamek;
      var alicznik1,amianownik1,alicznik2,amianownik2 :Integer;
      begin
      AUlamek1.GetValues(alicznik1,amianownik1);
      AUlamek2.GetValues(alicznik2,amianownik2);
      UlamekSum:=Ulamek.Create(alicznik1*amianownik2+alicznik2*amianownik1,amianownik1*amianownik2);
      end;

      {zmiana znaku}
      function UlamekRevr(const AUlamek1 :Ulamek) :Ulamek;
      var alicznik1,amianownik1 :Integer;
      begin
      AUlamek1.GetValues(alicznik1,amianownik1);
      UlamekRevr:=Ulamek.Create(-alicznik1,amianownik1);
      end;

      {roznica}
      function UlamekDiff(const AUlamek1, AUlamek2 :Ulamek) :Ulamek;
      var alicznik1,amianownik1,alicznik2,amianownik2 :Integer;
      begin
      AUlamek1.GetValues(alicznik1,amianownik1);
      AUlamek2.GetValues(alicznik2,amianownik2);
      UlamekDiff:=Ulamek.Create(alicznik1*amianownik2-alicznik2*amianownik1,amianownik1*amianownik2);
      end;

      {porownanie <, >, =}
      function UlamekCmp(const AUlamek1, AUlamek2 :Ulamek) :Integer;
      var alicznik1,amianownik1,alicznik2,amianownik2 :Integer;
          wynik :Double;
          int_wynik :Integer;
      begin
      AUlamek1.GetValues(alicznik1,amianownik1);
      AUlamek2.GetValues(alicznik2,amianownik2);
      wynik:=(alicznik1/amianownik1)-(alicznik1/amianownik2);
      if wynik=0 then int_wynik:=Round(wynik) else int_wynik:=Round(wynik/abs(wynik));
      UlamekCmp:=int_wynik;
      end;

      end.
  | C#
      public class Ulamek
      {
      	private int licznik,mianownik;

      	public static readonly Ulamek Zero=new Ulamek(0,1);
      	public static readonly Ulamek Jeden=new Ulamek(1,1);
      	public static readonly Ulamek Polowa=new Ulamek(1,2);
      	public static readonly Ulamek Cwierc=new Ulamek(1,4);

      	public Ulamek(int alicznik,int amianownik)
      		{
      		set(alicznik,amianownik);
      		}

      	public Ulamek(Ulamek aulamek)
      		{
      		int olicznik,omianownik;
      		aulamek.get(out olicznik,out omianownik);
      		set(olicznik,omianownik);
      		}

      	public Ulamek Clone()
      		{
      		return new Ulamek(licznik,mianownik);
      		}

      	public void set(int alicznik,int amianownik)
      		{
      		if (amianownik==0) throw new Ulamek_Exception("Ulamek: Blad #1");
      		licznik=alicznik;
      		mianownik=amianownik;
      		}

      	public void get(out int rlicznik,out int rmianownik)
      		{
      		rlicznik=licznik;
      		rmianownik=mianownik;
      		}

      	public void get(int[] arg)
      		{
      		arg[0]=licznik;
      		arg[1]=mianownik;
      		}

      	public int[] get()
      		{
      		int[] wynik=new int[2];
      		wynik[0]=licznik;
      		wynik[1]=mianownik;
      		return wynik;
      		}

      	public override string ToString()
      		{
      		return ""+licznik+"/"+mianownik;
      		}

      	public double ToDouble()
      		{
      		return licznik/(double)mianownik;
      		}

      	//konwersja z Ulamek na double
      	public static implicit operator double(Ulamek aulamek)
      		{
      		return aulamek.ToDouble();
      		}

      	//konwersja z int na Ulamek
      	public static implicit operator Ulamek(int aliczba_calkowita)
      		{
      		return new Ulamek(aliczba_calkowita,1);
      		}
      }
  Przykład kodu
      ShowMessage((double)ulamek(1,2));
      ShowMessage((double)ulamek(1,3));

      ShowMessage((char*)ulamek(1,2));
      ShowMessage((char*)ulamek(1,3));
  | Przykład kodu:
        Ulamek a=null,b=null;
        try
          {
          a=new Ulamek(1,2);
          b=new Ulamek(1,3);
          }
        catch(Exception E)
          {
          list1.add(E.getMessage());
          }

        list1.add("a: "+String.valueOf(a.toDouble()));
        list1.add("b: "+String.valueOf(b.toDouble()));

        list1.add("a: "+a.toString()); //jawna konwersja na String
        list1.add("b: "+b.toString());

        list1.add("a: "+String.valueOf(a)); //jawna konwersja na String
        list1.add("b: "+String.valueOf(b)); //korzysta z toString

        list1.add("a: "+a); //niejawna konwersja na String
        list1.add("b: "+b); //korzysta z toString
  | Pascal
      procedure TForm1.Button1Click(Sender: TObject);
      var a,b :Ulamek;
      begin
      a:=Ulamek.Create(1,2);
      b:=Ulamek.Create(1,3);

      ShowMessage('a: '+FloatToStr(a.ToFloat)+', b: '+FloatToStr(b.ToFloat));
      ShowMessage('a: '+a.ToString+', b: '+b.ToString);

      ShowMessage('a: '+FloatToStr(UlamekToFloat(a))+', b: '+FloatToStr(UlamekToFloat(b)));
      ShowMessage('a: '+UlamekToStr(a)+', b: '+UlamekToStr(b));
      end;
  | Przykład kodu:
      MessageBox.Show(Ulamek.Polowa.ToString());
      MessageBox.Show(""+Ulamek.Polowa);

      MessageBox.Show(""+Ulamek.Polowa.ToDouble());
      MessageBox.Show(""+(1.0+Ulamek.Polowa)); //niejawna konwersja na double

      Ulamek a=2;
      MessageBox.Show(""+a);

  Uwaga!

  - Dzięki konwersji mogę wykonywać na obiekcie klasy ulamek wykonywać funkcje zdefiniowane dla double i char*, np. (float)a, floor(a), cout << a << "\n" (dla zdefiniowanego obiektu a typu ulamek).
  - Więcej niż jeden operator konwersji może prowadzić do niejednoznaczności (która jest zawsze wykrywana przez kompilator i sygnalizowana jako błąd), wówczas niejawną konwersję trzeba zastąpić jawną.
  - Java: Jeżeli klasa zawiera metodę toString() (posiada ją już klasa Obiekt) wówczas operacje + oraz +=, których wartościami ma być String, a ich jednym lub obydwoma argumentami ma być nasz obiekt, wywołuje tą funkcję, aby wykonać niejawne rzutowanie na String.
  - C#: Podobnie jak w Javie, metoda ToString() służy także do automatycznej konwersji - nie trzeba już nic wiecej.
  - C#: W klasie można definiować operatory konwersji z i do typu implementowanego przez klasę.

  #### Metoda "uproszczenie ułamka":

  Elegancja klasy wymaga metody porządkującej i upraszczającej licznik i mianownik. Znak minus powinien znajdować się tylko przy liczniku, licznik i mianownik powinny mieć najmniejszą możliwą wartość (należy znaleźć NWD).
  Do szukania NWD istnieje wiele wyrafinowanych algorytmów, tutaj zadowolę się najprostszym jaki przyszedł mi do głowy.

  C++
      void simplify()
          {
          //NWD
          int mniejsza=(abs(licznik)<abs(mianownik))
      	             ?abs(licznik):abs(mianownik);
          for(int i=mniejsza;i>0;i--)
              if ((licznik%i==0) && (mianownik%i==0))
                  {
                  licznik/=i;
                  mianownik/=i;
                  }
           //znaki
           if (licznik*mianownik<0)
               {
               licznik=-abs(licznik);
               mianownik=abs(mianownik);
               }
               else
               {
               licznik=abs(licznik);
               mianownik=abs(mianownik);
               }
          }
  | Java
      public void simplify()
        {
        //NWD
        int mniejsza=(Math.abs(licznik)<Math.abs(mianownik))
                     ?Math.abs(licznik):Math.abs(mianownik);
        for(int i=mniejsza;i>0;i--)
            if ((licznik%i==0) && (mianownik%i==0))
                {
                licznik/=i;
                mianownik/=i;
                }
         //znaki
         if (licznik*mianownik<0)
             {
             licznik=-Math.abs(licznik);
             mianownik=Math.abs(mianownik);
             }
             else
             {
             licznik=Math.abs(licznik);
             mianownik=Math.abs(mianownik);
             }
          }
  | Pascal
      procedure Ulamek.Simplify;
      var mniejsza,i :Integer;
      begin
      //NWD
      mniejsza:=Min(abs(licznik),abs(mianownik));

      for i:=mniejsza downto 1 do
          if (licznik mod i=0) and (mianownik mod i=0) then
              begin
              licznik:=licznik div i;
              mianownik:=mianownik div i;
              end;

      //znaki
      if licznik*mianownik<0 then
          begin
          licznik:=-abs(licznik);
          mianownik:=abs(mianownik);
          end
          else
          begin
          licznik:=abs(licznik);
          mianownik:=abs(mianownik);
          end;
      end;
  | C#
      public void simplify()
      	{
      	//NWD
      	int mniejsza=(Math.Abs(licznik)0;i--)
      		if ((licznik%i==0) && (mianownik%i==0))
      			{
      			licznik/=i;
      			mianownik/=i;
      			}

      	//znaki
      	if (licznik*mianownik<0)
      		{
      		licznik=-Math.Abs(licznik);
      		mianownik=Math.Abs(mianownik);
      		}
      		else
      		{
      		licznik=Math.Abs(licznik);
      		mianownik=Math.Abs(mianownik);
      		}
      	}
  Przykład kodu
      ulamek a(30,-75);

      int licznik,mianownik;
      a.get(licznik,mianownik);
      ShowMessage((AnsiString)"nieuproszczone a: "+licznik+"/"+mianownik);

      a.simplify();

      a.get(licznik,mianownik);
      ShowMessage((AnsiString)"uproszczone a: "+licznik+"/"+mianownik);
  | Przykład kodu
      Ulamek a=null,b=null;
      try
        {
        a=new Ulamek(30,-75);
        }
      catch(Exception E)
        {
        list1.add(E.getMessage());
        }

      list1.add("a: "+a);
      a.simplify();

      list1.add("b: "+a);
  | Pascal
      procedure TForm1.Button1Click(Sender: TObject);
      var a :Ulamek;
      begin
      a:=Ulamek.Create(30,-75);

      ShowMessage('nieuproszczone a: '+a.ToString());

      a.Simplify();

      ShowMessage('uproszczone a: '+a.ToString());
      end;
  | Przykład kodu
      Ulamek a=new Ulamek(128,-96);
      a.simplify();
      MessageBox.Show(""+a);

  Uwaga!
  Mozna uczynić simplify() metodą prywatna, a za to wywolywac ja przy kazdym get(). Tutaj możliwość wykorzystania tej metody pozostawiona została użytkownikowi klasy.

  #### Rozdzielenie deklaracji i definicji klasy, pliki nagłówkowy i źródłowy:

  C++: W C++ zazwyczaj klasy umieszcza się w dwóch oddzielnych plikach tak, żeby rozdzielić deklarację umieszczoną w pliku nagłówkowym (ang. header file) od definicji znajdującej się w pliku źródłowym (ang. source file). Metody, których definicja pozostawiona zostanie w pliku nagłówkowym w obrębie deklaracji pozostają metodami inline. Metody przeniesione do pliku źródłowego przestają być inline, chyba, że użyte zostanie słowo kluczowe inline w ich definicji. Rozdzielenie plików deklaracji i definicji jest dobrym zwyczajem C++ pozwalającym między innymi na uproszczoną dystrybucję klas/bibliotek klas.

  Delphi: W Delphi wewnątrz modułu rozdzielenie deklaracji i definicji jest obowiązkowe ze względu na gramatykę Pascala.

  Java: W Javie definicja metod musi znajdować się w obrębie deklaracji klasy. Nie oznacza to wcale, że są metodami inline.

  C++ Plik nagłówkowy (ulamek.h):
      const ulamek_maxstr=1024;

      class ulamek
          {
          private:
          	int licznik, mianownik;
              double to_double() const;
              char* to_string() const;
              AnsiString to_AnsiString() const;
          public:
          	ulamek(int alicznik=0,int amianownik=1)
                  {
                  set(alicznik,amianownik);
                  }
          	ulamek(const ulamek& aulamek)
                  {
                  aulamek.get(licznik,mianownik);
                  }
          	void set(int alicznik,int amianownik);
              void simplify();
              void get(int& rlicznik,int& rmianownik) const;
              void get(int* plicznik,int* pmianownik) const;
              operator double() const {return to_double();}
              operator char*() const {return to_string();}
          };

      const ulamek ulamek_zero(0,1);
      const ulamek ulamek_jeden(1,1);
      const ulamek ulamek_polowa(1,2);
      const ulamek ulamek_cwierc(1,4);
  Plik źródłowy (ulamek.cpp):
      #include "ulamek.h"

      inline double ulamek::to_double() const
          {
          return licznik/(double)mianownik;
          }
      char* ulamek::to_string() const
          {
          char str_licznik[ulamek_maxstr]; itoa(licznik,str_licznik,10);
          char str_mianownik[ulamek_maxstr]; itoa(mianownik,str_mianownik,10);
          char* str=str_licznik;
          strcat(str,"/");
          strcat(str,str_mianownik);
          return str;
          }
      void ulamek::set(int alicznik,int amianownik)
          {
          if (amianownik==0) throw "Ulamek: Blad #1";
          licznik=alicznik;
          mianownik=amianownik;
          }
      void ulamek::simplify()
          {
          //NWD
          int mniejsza=(licznik<mianownik)?licznik:mianownik;
          for(int i=mniejsza;i>0;i--)
              if ((licznik%i==0) && (mianownik%i==0))
                      {
                      licznik/=i;
                      mianownik/=i;
                      }
                  //znaki
              if (licznik*mianownik<0)
                      {
                      licznik=-abs(licznik);
                      mianownik=abs(mianownik);
                      }
                      else
                      {
                      licznik=abs(licznik);
                      mianownik=abs(mianownik);
                      }
               }
      inline void ulamek::get(int& rlicznik,int& rmianownik) const
          {
          rlicznik=licznik;
          rmianownik=mianownik;
          }
      inline void ulamek::get(int* plicznik,int* pmianownik) const
          {
          *plicznik=licznik;
          *pmianownik=mianownik;
          }

      ulamek operator *(const ulamek& aulamek1,const ulamek& aulamek2)
          {
          int alicznik1=0,amianownik1=1;aulamek1.get(alicznik1,amianownik1);
          int alicznik2=0,amianownik2=1;aulamek2.get(alicznik2,amianownik2);
          return ulamek(alicznik1*alicznik2,amianownik1*amianownik2);
          };
      ulamek operator /(const ulamek& aulamek1,const ulamek& aulamek2)
          {
          int alicznik1=0,amianownik1=1;aulamek1.get(alicznik1,amianownik1);
          int alicznik2=0,amianownik2=1;aulamek2.get(alicznik2,amianownik2);
          return ulamek(alicznik1*amianownik2,amianownik1*alicznik2);
          };
      ulamek operator +(const ulamek& aulamek1,const ulamek& aulamek2)
          {
          int alicznik1=0,amianownik1=1;aulamek1.get(alicznik1,amianownik1);
          int alicznik2=0,amianownik2=1;aulamek2.get(alicznik2,amianownik2);
          return ulamek(alicznik1*amianownik2+alicznik2*amianownik1,amianownik1*amianownik2);
          };
      ulamek operator -(const ulamek& aulamek1)
          {
          int alicznik1=0,amianownik1=1;aulamek1.get(alicznik1,amianownik1);
          return ulamek(-alicznik1,amianownik1);
          };
      ulamek operator -(const ulamek& aulamek1,const ulamek& aulamek2)
          {
          int alicznik1=0,amianownik1=1;aulamek1.get(alicznik1,amianownik1);
          int alicznik2=0,amianownik2=1;aulamek2.get(alicznik2,amianownik2);
          return ulamek(alicznik1*amianownik2-alicznik2*amianownik1,amianownik1*amianownik2);
          };
      bool operator <(const ulamek& aulamek1,const ulamek& aulamek2)
          {
          int alicznik1=0,amianownik1=1;aulamek1.get(alicznik1,amianownik1);
          int alicznik2=0,amianownik2=1;aulamek2.get(alicznik2,amianownik2);
          return (alicznik1/(double)amianownik1)<(alicznik2/(double)amianownik2);
          };
      bool operator >(const ulamek& aulamek1,const ulamek& aulamek2)
          {
          int alicznik1=0,amianownik1=1;aulamek1.get(alicznik1,amianownik1);
          int alicznik2=0,amianownik2=1;aulamek2.get(alicznik2,amianownik2);
          return (alicznik1/(double)amianownik1)>(alicznik2/(double)amianownik2);
          };
      bool operator ==(const ulamek& aulamek1,const ulamek& aulamek2)
          {
          int alicznik1=0,amianownik1=1;aulamek1.get(alicznik1,amianownik1);
          int alicznik2=0,amianownik2=1;aulamek2.get(alicznik2,amianownik2);
          return (alicznik1/(double)amianownik1)==(alicznik2/(double)amianownik2);
          };
  Przykład kodu
      //TABLICE ULAMKOW
      //ShowMessage("Zakres liczb pseudolosowych: "+(AnsiString)(double)RAND_MAX);
      const zakres=100;
      const ilosc=10;
      ulamek a[ilosc];
      randomize();
      for(int i=0;i<ilosc;i++)
          {
          ulamek b=ulamek(rand() % zakres,rand() % zakres);
          a[i]=b;
          a[i].simplify();
          ShowMessage((AnsiString)(char*)b+" = "+(char*)a[i]);
          }

      //TYPOWE ZASTOSOWANIA
      ShowMessage((double)ulamek_polowa+0.1);
      ShowMessage(1/2);
      ulamek c=3/4;
      ShowMessage((char*)c);
      c=(ulamek)3/(ulamek)4;
      ShowMessage((char*)c);

  Uwaga!
  C++: Używanie metod inline w przypadku bardziej złożonych funkcji nie jest zalecane - utrudnia to kompilatorowi optymalizację kodu wynikowego.

  #### Szablon klasy:

  Przygotujemy szablon (tylko C++):
  1. Przed deklaracją klasy dodajemy: template <class T>
  2. T powinno zastąpić wszystkie użycia int w obrębie deklaracji.
  3. W definicji przed każdą metodą nalezy użyć template <class T> oraz przy każdym ułamek dodać specyfikację typu <T> (tzn. ulamek<T>).

  C++ Plik nagłówkowy (ulamek.h):
      #include

      const ulamek_maxstr=1024;

      template <class T>
      class ulamek
          {
          private:
          	T licznik, mianownik;
              double to_double() const;
              char* to_string() const;
          public:
          	ulamek(T alicznik=0,T amianownik=1)
                  {
                  //Szablon obsluguje tylko typy calkowite
      			if (!(
                  	typeid(T)==typeid(unsigned char) ||
                      typeid(T)==typeid(char) ||
                      typeid(T)==typeid(short int) ||
                      typeid(T)==typeid(unsigned int) ||
                      typeid(T)==typeid(int) ||
                      typeid(T)==typeid(unsigned long) ||
                      typeid(T)==typeid(long)
                     )) throw "Ulamek: Blad #2";

                  set(alicznik,amianownik);
                  }
          	ulamek(const ulamek& aulamek)
                  {
                  aulamek.get(licznik,mianownik);
                  }
          	void set(T alicznik,T amianownik);
              void simplify();
              void get(T& rlicznik,T& rmianownik) const;
              void get(T* plicznik,T* pmianownik) const;
              operator double() const {return to_double();}
              operator char*() const {return to_string();}
          };

      /*
      //Szablony muszą być klasami lub funkcjami
      const ulamek ulamek_zero(0,1);
      const ulamek ulamek_jeden(1,1);
      const ulamek ulamek_polowa(1,2);
      const ulamek ulamek_cwierc(1,4);
      */
  Plik główny (ulamek.cpp):
      #include "ulamek.h"

      template <class T> inline double ulamek<T>::to_double() const
          {
          return licznik/(double)mianownik;
          }
      template <class T> char* ulamek<T>::to_string() const
          {
          char str_licznik[ulamek_maxstr]; itoa(licznik,str_licznik,10);
          char str_mianownik[ulamek_maxstr]; itoa(mianownik,str_mianownik,10);
          char* str=str_licznik;
          strcat(str,"/");
          strcat(str,str_mianownik);
          return str;
          }
      template <class T> void ulamek<T>::set(T alicznik,T amianownik)
          {
          if (amianownik==0) throw "Ulamek: Blad #1";
          licznik=alicznik;
          mianownik=amianownik;
          }
      template <class T> void ulamek<T>::simplify()
          {
          //NWD
          T mniejsza=(abs(licznik)<abs(mianownik))?abs(licznik):abs(mianownik);
          for(T i=mniejsza;i>0;i--)
              if ((licznik%i==0) && (mianownik%i==0))
                      {
                      licznik/=i;
                      mianownik/=i;
                      }
                  //znaki
              if (licznik*mianownik<0)
                      {
                      licznik=-abs(licznik);
                      mianownik=abs(mianownik);
                      }
                      else
                      {
                      licznik=abs(licznik);
                      mianownik=abs(mianownik);
                      }
               }
      template <class T> inline void ulamek<T>::get(T& rlicznik,T& rmianownik) const
          {
          rlicznik=licznik;
          rmianownik=mianownik;
          }
      template <class T> inline void ulamek<T>::get(T* plicznik,T* pmianownik) const
          {
          *plicznik=licznik;
          *pmianownik=mianownik;
          }

      template <class T> ulamek<T> operator *(const ulamek<T>& aulamek1,const ulamek<T>& aulamek2)
          {
          T alicznik1=0,amianownik1=1;aulamek1.get(alicznik1,amianownik1);
          T alicznik2=0,amianownik2=1;aulamek2.get(alicznik2,amianownik2);
          return ulamek<T>(alicznik1*alicznik2,amianownik1*amianownik2);
          };
      template <class T> ulamek<T> operator /(const ulamek<T>& aulamek1,const ulamek<T>& aulamek2)
          {
          T alicznik1=0,amianownik1=1;aulamek1.get(alicznik1,amianownik1);
          T alicznik2=0,amianownik2=1;aulamek2.get(alicznik2,amianownik2);
          return ulamek<T>(alicznik1*amianownik2,amianownik1*alicznik2);
          };
      template <class T> ulamek<T> operator +(const ulamek<T>& aulamek1,const ulamek<T>& aulamek2)
          {
          T alicznik1=0,amianownik1=1;aulamek1.get(alicznik1,amianownik1);
          T alicznik2=0,amianownik2=1;aulamek2.get(alicznik2,amianownik2);
          return ulamek<T>(alicznik1*amianownik2+alicznik2*amianownik1,amianownik1*amianownik2);
          };
      template <class T> ulamek<T> operator -(const ulamek<T>& aulamek1)
          {
          T alicznik1=0,amianownik1=1;aulamek1.get(alicznik1,amianownik1);
          return ulamek<T>(-alicznik1,amianownik1);
          };
      template <class T> ulamek<T> operator -(const ulamek<T>& aulamek1,const ulamek<T>& aulamek2)
          {
          T alicznik1=0,amianownik1=1;aulamek1.get(alicznik1,amianownik1);
          T alicznik2=0,amianownik2=1;aulamek2.get(alicznik2,amianownik2);
          return ulamek<T>(alicznik1*amianownik2-alicznik2*amianownik1,amianownik1*amianownik2);
          };
      template <class T> bool operator <(const ulamek<T>& aulamek1,const ulamek<T>& aulamek2)
          {
          T alicznik1=0,amianownik1=1;aulamek1.get(alicznik1,amianownik1);
          T alicznik2=0,amianownik2=1;aulamek2.get(alicznik2,amianownik2);
          return (alicznik1/(double)amianownik1)<(alicznik2/(double)amianownik2);
          };
      template <class T> bool operator >(const ulamek<T>& aulamek1,const ulamek<T>& aulamek2)
          {
          T alicznik1=0,amianownik1=1;aulamek1.get(alicznik1,amianownik1);
          T alicznik2=0,amianownik2=1;aulamek2.get(alicznik2,amianownik2);
          return (alicznik1/(double)amianownik1)>(alicznik2/(double)amianownik2);
          };
      template <class T> bool operator ==(const ulamek<T>& aulamek1,const ulamek<T>& aulamek2)
          {
          int alicznik1=0,amianownik1=1;aulamek1.get(alicznik1,amianownik1);
          int alicznik2=0,amianownik2=1;aulamek2.get(alicznik2,amianownik2);
          return (alicznik1/(double)amianownik1)==(alicznik2/(double)amianownik2);
          };
  Przykład kodu
      typedef ulamek<long> long_ulamek;

      const long_ulamek long_ulamek_zero(0,1);
      const long_ulamek long_ulamek_jeden(1,1);
      const long_ulamek long_ulamek_polowa(1,2);
      const long_ulamek long_ulamek_cwierc(1,4);

      const zakres=100;
      const ilosc=10;
      try
          {
          long_ulamek a[ilosc];
          randomize();
          for(int i=0;i<ilosc;i++)
          	{
              long_ulamek b=long_ulamek(rand() % zakres,rand() % zakres);
          	a[i]=b;
          	a[i].simplify();
          	ShowMessage((AnsiString)(char*)b+" = "+(char*)a[i]);
          	}
          }
      catch(char* E)
          {
          ShowMessage(E);
          exit(EXIT_FAILURE);
          }

  Uwaga!

  - W przykładowym kodzie, poza demonstracją szablonów, pokazana jest możliwość tworzenia tablic obiektów. Nie wymaga to od klasy żadnych specjalnych przygotowań (nie można tylko nadpisać operatora []).
  - C++: W konstruktorze dodano weryfikację, czy typ T jest jednym ze znanych typów całkowitych. W tym celu wykorzystana jest funkcja, która stosunkowo niedawno znajduje się w standardzie C++ typeid().
  - Delphi: Odpowiednikiem funkcji typeid w C++ jest operator is w Object Pascalu. np: if a is Ulamek then ...
  - W szablonach nie można zdefiniować stałych, szablony dotyczą tylko dla klas i funkcji. Definicje stałych zostały więc usunięte z pliku nagłówkowego.

  #### Dziedziczenie publiczne:

  Dziedziczenie/rozszerzanie klas jest sposobem na uwspólnianie części kodu, hermetyzację implementacji oraz wykorzystanie innych zalet dziedziczenia (np. poliformizmu). Jest również dobrym zwyczajem umieszczanie w klasie bazowej silnika działania klasy, a w klasie potomnej interface'u. Pomaga to uchronić się od błędu i od dostosowywania implementacji do potrzeb drugorzędnych.
  Aby dziedziczyć z klasy ulamek, musimy jej implementację umieszczoną w części prywatnej "awansować" do sekcji chronionej. W praktyce oznacza to zamianę słowa kluczowego private na protected.
  W przypadku operatorów zdefiniowanych poza klasą pojawi się problem: wartość zwracana przez te operatory jest obiektem klasy bazowej, a nie potomnej. Operatory powinny być powtórzone dla nowej klasy. Można w takim przypadku skorzystać z operatorów dla klasy bazowej, ale należy do klasy dodać funkcję pozwalającą na konwersję klasy bazowej na potomną (konwersja odwrotna jest automatyczna).

  C++
      template <class T>
      class ansistring_ulamek : public ulamek<T>
          {
          private:
              AnsiString to_AnsiString() const
                  {
                  return (AnsiString)licznik+'/'+(AnsiString)mianownik;
                  }
          public:
          	ansistring_ulamek(T alicznik=0,T amianownik=1):ulamek<T>(alicznik,amianownik)
                  {
                  }
              from_ulamek(ulamek<T> aulamek)
                  {
                  aulamek.get(licznik,mianownik);
                  }
              operator AnsiString()
                  {
                  return to_AnsiString();
                  }
          };

      //operatory zwracajace obiekt klasy potomnej
      template <class T> ansistring_ulamek<T> operator *(const ansistring_ulamek<T>& aulamek1,
                                                         const ansistring_ulamek<T>& aulamek2)
          {
          ansistring_ulamek<T> as_ulamek;
          as_ulamek.from_ulamek((ulamek<T>)aulamek1*(ulamek<T>)aulamek2);
          return as_ulamek;
          };
      template <class T> ansistring_ulamek<T> operator /(const ansistring_ulamek<T>& aulamek1,
                                                         const ansistring_ulamek<T>& aulamek2)
          {
          ansistring_ulamek<T> as_ulamek;
          as_ulamek.from_ulamek((ulamek<T>)aulamek1/(ulamek<T>)aulamek2);
          return as_ulamek;
          };
      template <class T> ansistring_ulamek<T> operator +(const ansistring_ulamek<T>& aulamek1,
                                                         const ansistring_ulamek<T>& aulamek2)
          {
          ansistring_ulamek<T> as_ulamek;
          as_ulamek.from_ulamek((ulamek<T>)aulamek1+(ulamek<T>)aulamek2);
          return as_ulamek;
          };
      template <class T> ansistring_ulamek<T> operator -(const ansistring_ulamek<T>& aulamek1)
          {
          ansistring_ulamek<T> as_ulamek;
          as_ulamek.from_ulamek(-(ulamek<T>)aulamek1);
          return as_ulamek;
          };
      template <class T> ansistring_ulamek<T> operator -(const ansistring_ulamek<T>& aulamek1,
                                                         const ansistring_ulamek<T>& aulamek2)
          {
          ansistring_ulamek<T> as_ulamek;
          as_ulamek.from_ulamek((ulamek<T>)aulamek1-(ulamek<T>)aulamek2);
          return as_ulamek;
          };
  | Java (plik CString_Ulamek.java)
      package klasy;

      public class CString_Ulamek extends Ulamek implements Cloneable
      {
        public CString_Ulamek(int alicznik,int amianownik)
          throws Exception
          {
          super(alicznik,amianownik);
          }

        public char[] toCString()
          {
          String swynik=this.toString();
          char[] cwynik = new char[swynik.length()];
          swynik.getChars(0, swynik.length(), cwynik, 0);
          return cwynik;
          }
      }
  | Pascal
      unit _cstring_ulamek;

      interface

      uses _ulamek, SysUtils;
      type
      CString = array of Char; //tablica dynamiczna
      PCString = ^CString;

      CString_Ulamek = class(Ulamek)
          public
              constructor Create(const alicznik :Integer =0; const amianownik :Integer =1);
              procedure ToCString(const buffer_pointer :PCString;var buffer_length :Integer);
              procedure ToPChar(const buffer_pointer :PChar;const buffer_length :Integer);
          end;

      implementation

      constructor CString_Ulamek.Create(const alicznik :Integer =0; const amianownik :Integer =1);
      begin
      inherited; //Ulamek.Create(licznik,mianownik)
      end;

      procedure CString_Ulamek.ToCString(const buffer_pointer :PCString;var buffer_length :Integer);
      var s :String;
          i :Integer;
      begin
      s:=IntToStr(licznik)+'/'+IntToStr(mianownik);
      buffer_length:=Length(s);
      SetLength(buffer_pointer^,buffer_length);
      for i:=0 to buffer_length-1 do buffer_pointer^[i]:=s[i+1]; //s numerowane od 1
      end;

      procedure CString_Ulamek.ToPChar(const buffer_pointer :PChar;const buffer_length :Integer);
      var s :String;
          i :Integer;
      begin
      s:=IntToStr(licznik)+'/'+IntToStr(mianownik);
      StrPLCopy(buffer_pointer,s,buffer_length);
      end;

      {Tu powinny byc operatory dla CString_Ulamek}

      end.
  | C#
      public class CString_Ulamek : Ulamek
      {
      	public CString_Ulamek(int alicznik,int amianownik):base(alicznik,amianownik)
      		{
      		}

      	public char[] ToCString()
      		{
      		return this.ToString().ToCharArray();
      		}

      	public static explicit operator char[](CString_Ulamek acstring_ulamek)
      		{
      		return acstring_ulamek.ToCString();
      		}
      }
  Przykład kodu
      ulamek a=ulamek(1,2);
      ShowMessage((char*)a);

      ansistring_ulamek b=ansistring_ulamek(3,4);
      ShowMessage(b);

      ansistring_ulamek d1=3, d2=4;
      ShowMessage(d1+d2);
      ShowMessage(-d1);
      ShowMessage(d1-d2);
      ShowMessage(d1*d2);
      ShowMessage(d1/d2);
  | Przykład kodu
      CString_Ulamek a=null;
      try{a=new CString_Ulamek(30,-75);}catch(Exception E){list1.add(E.getMessage());}
      a.simplify();
      char[] c=a.toCString();
      list1.add("a: "+String.valueOf(c));
  | Pascal
      procedure TForm1.Button1Click(Sender: TObject);
      var a :CString_Ulamek;
          buffer :CString;
          buffer1 :array of Char;
          buffer_size :Integer;
          s :String;
      begin
      a:=CString_Ulamek.Create(1,2);

      a.ToCString(@buffer,buffer_size);
      ShowMessage('dlugosc bufora: '+IntToStr(buffer_size));
      ShowMessage('zawartosc bufora: '+String(buffer));

      a.ToCString(@s,buffer_size);
      ShowMessage('dlugosc bufora: '+IntToStr(buffer_size));
      ShowMessage('zawartosc bufora: '+s);

      a.ToPChar(PChar(buffer),Length(buffer));
      ShowMessage('dlugosc bufora: '+IntToStr(Length(buffer)));
      ShowMessage('zawartosc bufora: '+String(buffer));
      end;
  | Przykład kodu
      CString_Ulamek a=new CString_Ulamek(1,2);
      char[] ac=a.ToCString();
      MessageBox.Show(""+ac[1]);
      MessageBox.Show(""+((char[])a)[1]);

  Uwaga!

  - Jeżeli klasa bazowa posiada konstruktor domyślny, może on być automatycznie wywołany przez stworzony przez kompilator domyślny konstruktor klasy bazowej. Jeżeli tworzymy sami konstruktor i/lub klasa bazowa posiada konstruktor z argumentami, należy go wywołać samemu. W Javie odwołanie musi znajdować się w pierwszej linii konstruktora (super(arg);), w Pascalu też jest to wskazane (inherited Bazowa(arg);). Natomiast w C++ wywołanie konstruktora może znajdować się za sygnaturą konstruktora bazowego lub w jego ciele.
  - C++: Klasa potomna może oczywiście nie być szablonem. Wówczas musimy z szablonu klasy bazowej wybrać jeden typ, np. int

        class ansistring_int_ulamek : public ulamek
            {
            private:
                AnsiString to_AnsiString() const
                    {
                    return (AnsiString)licznik+'/'+(AnsiString)mianownik;
                    }
            public:
            	ansistring_int_ulamek(int alicznik=0,int amianownik=1):ulamek(alicznik,amianownik)
                    {
                    }
                operator AnsiString()
                    {
                    return to_AnsiString();
                    }
            };

  - Delphi: Osoby niechętne korzystaniu z funkcji Pascala operujących na null-terminated strings mogą metodę ToPChar zaimplementować w następujący sposób:

        procedure CString_Ulamek.ToPChar(const buffer_pointer :PChar;const buffer_length :Integer);
        var s :String;
            i :Integer;
        begin
        s:=IntToStr(licznik)+'/'+IntToStr(mianownik);
        i:=0;
        while (i<buffer_length) and (i<Length(s)) do
                begin
                (buffer_pointer+i)^:=s[i+1]; //s numerowane od 1
                Inc(i);
                end;
        (buffer_pointer+i)^:=#0;
        end;

  - Java: Deklarując klasę jako final wyklucza się jej dalsze rozszerzanie.

  Nieomówione zagadnienia dotyczące klas:
  - Destruktor
  - Polimorfizm, funkcje wirtualne, klasy abstrakcyjne
  - Dziedziczenie prywatne w C++
  - Dziedziczenie wieloobrazowe (C++), interfejsy (Java)

  (c) Jacek Matulewski 2003-2004 (jacek@phys.uni.torun.pl)
  wersja z dnia: 17 VIII 2004