Programowanie obiektowe w językach C++ i Python

przez | 2 lutego 2021
Treści dotyczące stosowania zasad programowania obiektowego w rozwiązywaniu problemów są nieobowiązkowe w związku z usunięciem odpowiednich treści z podstawy programowej zgodnie z rozporządzeniem MEN z 2024 roku.
Temat ten można potraktować jako temat dodatkowy, uzupełniający.

Wszystkie treści na stronie ir.migra.pl chronione są prawami autorskimi. Więcej informacji znajdziesz tutaj.

Uwaga: Zapoznaj się wcześniej z tematem C2 z podręcznika „Teraz bajty. Informatyka dla szkół ponadpodstawowych. Zakres podstawowy. Klasa II” albo z tematami 61-62 (C++) lub z tematami 73-74 (Python) z podręcznika „Informatyka 1-3. Podręcznik dla szkół ponadpodstawowych. Zakres podstawowy.” Wykonaj zawarte tam ćwiczenia i zadania.

Spis treści

  1. Podstawowe pojęcia programowania obiektowego
  2. Tworzenie klas w języku C++
  3. Tworzenie klas w języku Python
  4. Cechy programowania obiektowego
    1. Hermetyzacja / enkapsulacja
    2. Dziedziczenie
    3. Polimorfizm
    4. Metody statyczne

1. Podstawowe pojęcia programowania obiektowego

U podstaw programowania obiektowego leży chęć ściślejszego związania rzeczywistości z jej abstrakcyjną reprezentacją w programie. Cel ten osiągnięto poprzez zdefiniowanie klas, obiektów, właściwości i metod.

Klasa to pewnego rodzaju wzorzec, określający, jakie dane dotyczące obiektu (zwane, tak jak w rekordach, polami lub właściwościami) są przechowywane i jakie operacje (zwane metodami) można na obiekcie wykonać.
Obiekt, zwany też niekiedy instancją klasy (ang. class instance), to konkretyzacja danej klasy, wypełnienie wzorca określonymi danymi.

Przykład 1. Określanie klasy, metod i obiektu

Człowiek jest klasą.

Klasa Człowiek posiada następujące właściwości:
    imię
    nazwisko
    data urodzenia
    wzrost
    waga

Człowiek potrafi też wykonywać następujące czynności (wykonywać metody):
    chodzić
    siedzieć
    leżeć
    pracować
    spać
    myśleć
    mówić

Adam_Abacki jest obiektem klasy Człowiek.
Adam_Abacki posiada cechy (pola) o następujących wartościach:
    imię: Adam
    nazwisko: Abacki
    data urodzenia: 6 czerwca 1996 roku
    wzrost: 186 cm
    waga: 76 kg
Adam_Abacki potrafi wykonywać te same czynności, co każdy inny obiekt klasy Człowiek, choć oczywiście sposób ich wykonania będzie zależeć od jego indywidualnych cech (np. chodzi szybko, śpi długo).

Ćwiczenie 1. Określamy właściwości metody przykładowej klasy

  1. Rozważ klasę Samochód.
  2. Zastanów się, jakie właściwości metody będzie zawierać klasa Samochód.

Pierwszym zastosowaniem programowania obiektowego były symulacje komputerowe, a pierwszym językiem obiektowym – język Simula. Przykładowe języki programowania obiektowego to: C++, C#, Python i Java. Wiele elementów programowania obiektowego znajdziemy także w językach Python, JavaScript, VisualBasic i PHP.

2. Tworzenie klas w języku C++

W języku C++ deklarację klasy zaczynamy od słowa kluczowego class. W jednej z powszechnie używanych konwencji nazewniczych nazwy klas rozpoczyna się literą „C”. W tym temacie nazwy klas będziemy zapisywać wielką literą (w języku C++  poprzedzając nazwę literą „C”). Nazwy metod będziemy zapisywać również wielkimi literami, a w przypadku nazw wieloczłonowych każdy człon będziemy zaczynać od wielkiej litery, nie stosując spacji.

Deklaracja klasy class CKlasa
{
    typ1 pole1;
    typ2 pole2;
...
    void Metoda1(typ_parametru parametr);
    typ_funkcji Metoda2(typ_parametru parametr);
...
}
C++

Obiektem w językach C++ jest zmienna typu klasy:

C++
nazwa_klasy nazwa_zmiennej;
np.:
CKlasa Obiekt;

W języku C++ do pól obiektu odwołujemy się, podając nazwę zmiennej, a po kropce – nazwę pola, np. Obiekt.pole1, Obiekt.pole2.

Ta sama zasada dotyczy wywoływania metod. Tu również podajemy nazwę obiektu, a po kropce – nazwę metody, uzupełnioną ewentualnie o parametry metody:

C++
Obiekt.Metoda1(parametr);

Każda metoda musi zostać zdefiniowana w programie

Definicja metody wygląda dokładnie tak, jak definicja funkcji, z tą tylko różnicą, że nazwę metody poprzedzamy nazwą klasy. Ponadto w treści metody można bezpośrednio, bez podawania nazwy klasy, odwoływać się do pól oraz innych metod tej samej klasy.

void CKlasa::Metoda1(typ_parametru parametr)
{
...
}
typ_funkcji CKlasa::Metoda2(typ_parametru parametr)
{
...
}

Uwaga: Parametry formalne metody powinny nosić inne nazwy niż pola składowe klasy.

Przykład 2. Definicja klasy CProstokat w języku C++

Zadeklarowano klasę CProstokat. Posiada ona pola w i h, przechowujące odpowiednio szerokość i wysokość prostokąta. Dodatkowo klasa posiada metody: Pole(), Obwod(). Metody Pole() i Obwod() zadeklarowano jako funkcje. Pokazano też definicję metody Pole klasy CProstokat – funkcja CProstokat::Pole().

C++
class CProstokat
{
public:
   float w, h; // Szerokość i wysokość
   float Pole();
   float Obwod();
};
// Oblicza pole
float CProstokat::Pole()
{
   return w * h;
}
// ... dalsze metody

Ćwiczenie 2. Analizujemy przykładową definicję klasy w języku C++

  1. Otwórz plik TC4_p2_c2.cpp. Przeanalizuj definicję klasy CProstokat i jej metod.
  2. Sprawdź, w jaki sposób utworzono obiekt Prostokat należący do tej klasy. Zapoznaj się ze sposobami korzystania z pól i metod klasy.

Ćwiczenie 3. Definiujemy klasę w języku C++

  1. Uzupełnij program zapisany w pliku TC4_p2_c2.cpp o definicję klasy COkrag, zawierającej te same metody, co klasa CProstokat.
  2. Zastanów się nad polem niezbędnym do opisania okręgu. Zdefiniuj metody.
  3. Zapisz program pod tą sama nazwą.

Przedstawione w przykładzie 2. definicje klasy CProstokat mają pewną wadę. Dostęp do pól klasy nie jest niczym ograniczony. Przypadkowo można zatem zmienić wartości, np. wysokości na wartość ujemną, która dla długości boku jest niepoprawna.

Ćwiczenie 4. Modyfikujemy program

  1. W programie zapisanym w pliku TC4_p2_c2.cpp przed instrukcją:
    Prostokat.Pole(); wstaw instrukcję: Prostokat.w := -10;
  2. Zaobserwuj, jak zmieni się działanie programu po wstawieniu podanej w punkcie 1. instrukcji.

Problemów z brakiem kontroli w dostępie do pól obiektu można uniknąć, ograniczając dostęp do pól.

W języku C++ pola i metody dzielimy na prywatne i publiczne i określamy jako takie, używając słów kluczowych: private i public. Dostęp do prywatnych pól i metod jest możliwy jedynie przez inne metody danej klasy. Istnieją również pole i metody chronione (słowo kluczowe protected).

Pola prywatne i metody prywatne są dostępne tylko dla metod należących do klasy.
Pole chronione i metody chronione są dostępne dla metod należących do klasy oraz klas pochodnych (punkt 4.2).
Pola publiczne i metody publiczne są dostępne bez powyższych ograniczeń.

Przykład 3. Deklarowanie pól prywatnych

Pola w i h klasy CProstokat zostały zadeklarowane jako prywatne. Poza klasą dostęp do nich nie będzie możliwy. Dodatkowo, dodano metody umożliwiające nadanie wartości zmiennym prywatnym, sprawdzające, czy nie są używane wartości ujemne.

C++
class CProstokat
{
private: // Pola i metody prywatne
   float w,h; // Szerokość i wysokość
public: // Pola i metody publiczne
   float Pole();
   float Obwod();
   void OkreslSzerokosc(float pW);
   void OkreslWysokosc(float pW);
};
// ...
void CProstokat::OkreslSzerokosc(float pW)
{
   w = (pW >= 0 ? pW : 0);
}
Klasa może posiadać konstruktor i destruktor. Konstruktor jest wywoływany automatycznie w momencie tworzenie obiektu (np. inicjalizacji zmiennej należącej do klasy), natomiast destruktor – automatycznie w momencie usuwania obiektu.

Konstruktor zwykle wykorzystuje się do nadania wartości początkowych polom obiektu, natomiast destruktor – do zwolnienia zasobów przydzielonych dodatkowo obiektowi (np. pamięci operacyjnej). Jeżeli nie określimy konstruktora i destruktora dla klasy, kompilator stworzy je automatycznie.

C++
class CProstokat
{
public:
   float w, h; // Szerokość i wysokość
   float Pole();
   float Obwod();
   CProstokat();   // Pierwszy konstruktor
   CProstokat(float pW, float pH);     // Drugi konstruktor
   ~CProstokat();  // Destruktor
};

Konstruktor deklarujemy jak inne metody, jednak bez określania typu metody, a jako nazwę podajemy nazwę klasy. Konstruktor może posiadać parametry. Może istnieć kilka konstruktorów, różniących się parametrami.

Destruktor nie posiada ani typu ani parametrów. Jako nazwę destruktora podajemy nazwę klasy, poprzedzoną znakiem tyldy („~”).

Jeżeli w ramach klasy zadeklarujemy istnienie konstruktora i destruktora, musimy je zdefiniować – tak, jak w przypadku każdej innej metody.

Przykład 4. Użycie destruktora

C++
CProstokat::CProstokat()
{
      w = h = 0;
}
CProstokat::CProstokat(float pW, float pH)
{
      w = pW;
      h = pH;
}
CProstokat::~CProstokat()
{
      // Destruktor nie wykonuje żadnych operacji
}

Przykład 5. Użycie konstruktorów

C++
int main()
{
      CProstokat prostokat1;    // Deklaracja "pustego" obiektu klasy CProstokat, pola w i h będą miały wartość 0
      CProstokat prostokat2(10, 5);   // Deklaracja obiektu klasy CProstokat, pole w będzie miało wartość 10, pole h – wartość 5
      cout << prostokat1.w << " " << prostokat1.h << endl;
      cout << prostokat2.w << " " << prostokat2.h << endl;

}

Ćwiczenie 5. Tworzymy nowy program wykorzystujący klasy w języku C++

  1. Umieść klasy CProstokat i COkrag w nowym programie i zadeklaruj wszystkie pola jako prywatne. Zapisz program pod nazwą TC4_c5.
  2. Zastanów się, jak musisz zmodyfikować definicje klas CProstokat i COkrag, aby w programie korzystającym z tych klas można było nadać obiektom początkowe wymiary oraz odczytać te wartości.

3. Tworzenie klas w języku Python

W języku Python (podobnie jak w języku C++) deklarację klasy zaczynamy od słowa kluczowego class.

W tym temacie nazwy klas będziemy zapisywać wielką literą. Nazwy metod w języku Python będziemy zapisywać małymi literami, a w przypadku nazw wieloczłonowych poszczególne człony nazwy będziemy rozdzielać znakiem podkreślenia.

W języku Python pola obiektu (podobnie jak zmienne lokalne) są tworzone w momencie pierwszego przypisania im wartości i dlatego nie trzeba ich uprzednio deklarować jak w C++.

Deklaracja klasyclass Klasa:
    def __init__(self, wartosc1, wartosc2):
        self.pole1 = wartosc1
        self.pole2 = wartosc2

    def metoda1(self, parametr):
        ...

W języku Python obiektem jest zmienna, do której została przypisana instancja klasy, np. po jej stworzeniu:

obiekt = Klasa()

W języku Python do pól obiektu odwołujemy się, podając nazwę zmiennej, a po kropce – nazwę pola, np. Obiekt.pole1, Obiekt.pole2.

Ta sama zasada dotyczy wywoływania metod. Tu również podajemy nazwę obiektu, a po kropce – nazwę metody, uzupełnioną ewentualnie o parametry metody:

obiekt.metoda1(parametr)

Każda metoda musi zostać w programie zdefiniowana.

W języku Python definicja metody znajduje się wewnątrz definicji klasy i  zawsze zawiera pierwszy parametr self, poprzez który można odwoływać się do pól i metod obiektu, dla którego wywołano metodę.

class Klasa:
    def metoda1(self, parametr):
        ...
    def metoda2(self, parametr):
        ...

Przykład 6. Definicja klasy Prostokat w języku Python

Zadeklarowano klasę Prostokat. Posiada ona pola w i h, przechowujące odpowiednio szerokość i wysokość prostokąta. Dodatkowo klasa posiada metody: pole(), obwod(). Metody pole() i obwod() zadeklarowano jako funkcje. Pokazano też definicję metody Pole klasy Prostokat – funkcja pole(self).

class Prostokat:
    def __init__(self, pW, pH):
        self.w = pW    # wysokość i szerokość
        self.h = pH  
    # oblicza pole
    def pole(self):
        return self.w * self.h   
    # dalsze metody
    ...

Ćwiczenie 6. Analizujemy przykładową definicję klasy w języku Python

  1. Otwórz plik TC4_p3_c6.py.
  2. Przeanalizuj definicję klasy Prostokat i jej metod. Sprawdź, w jaki sposób utworzono obiekt Prostokat należący do tej klasy. Zapoznaj się ze sposobami korzystania z pól i metod klasy.

Ćwiczenie 7. Definiujemy klasę w języku Python

  1. Uzupełnij program zapisany w pliku TC4_p3_c6.py o definicję klasy Okrag, zawierającej te same metody, co klasa Prostokat.
  2. Zastanów się nad polem niezbędnym do opisania okręgu. Zdefiniuj metody.

Przedstawione w przykładzie 2. definicje klasy Prostokat mają pewną wadę. Dostęp do pól klasy nie jest niczym ograniczony. Przypadkowo można zatem zmienić wartości, np. wysokości na wartość ujemną, która dla długości boku jest niepoprawna.

Ćwiczenie 8. Modyfikujemy program

  1. W programie zapisanym w pliku TC4_p2.py przed instrukcją:

    prostokat.pole() wstaw instrukcję prostokat.w = -10
  2. Zaobserwuj, jak zmieni się działanie programu po wstawieniu podanej w punkcie 1. instrukcji..

Problemów z brakiem kontroli w dostępie do pól obiektu można uniknąć, ograniczając dostęp do pól.

W języku Python nie ma możliwości ograniczenia dostępu do pól i metod klas (jak w języku C++), ale przyjmuje się, że nazwy metod, które nie powinny być używane spoza klasy, powinny zaczynać się od dwóch podkreślników.

Klasa może posiadać konstruktor i destruktor. Konstruktor jest wywoływany automatycznie w momencie tworzenie obiektu (np. inicjalizacji zmiennej należącej do klasy), natomiast destruktor – automatycznie w momencie usuwania obiektu. Konstruktor zwykle wykorzystuje się do nadania wartości początkowych polom obiektu, natomiast destruktor – do zwolnienia zasobów przydzielonych dodatkowo obiektowi (np. pamięci operacyjnej). Jeżeli nie określimy konstruktora i destruktora dla klasy, kompilator stworzy je automatycznie.

W języku Python rolę konstruktora spełnia specjalna metoda __init__.W odróżnieniu od C++, w języku Python można zadeklarować tylko jedną taką metodę.

class Prostokat:
    def __init__(self, pW, pH):
        self.w = pW   
        self.h = pH 

W języku Python istnieje automatyczne zarządzanie przydzielaniem i zwalnianiem pamięci i tylko w szczególnych przypadkach definiuje się destruktory. Wtedy implementujemy specjalną metodę __del__, której jedynym argumentem jest self, czyli obiekt, który jest usuwany.

= Prostokat(0, 0)
prostokat2 = Prostokat(10, 5)
print(prostokat1.w, prostokat1.h)
print(prostokat2.w, prostokat2.h)

Ćwiczenie 9. Tworzymy nowy program wykorzystujący klasy w języku Python

Umieść klasy Prostokat i Okrag w nowym programie. Zastanów się, jak musisz zmodyfikować definicje klas Prostokat i Okrag, aby w programie korzystającym tych klas można było nadać obiektom początkowe wymiary oraz odczytać te wartości.

4.    Cechy programowania obiektowego

4.1. Hermetyzacja / enkapsulacja

Programowanie z wykorzystaniem klas i obiektów posiada wiele zalet. Dane połączone są z wykonywanymi na nich operacjami poprzez zgrupowanie pól i metod w klasach. Użycie dyrektywy private pozwala zabezpieczyć dane obiektu przed przypadkowymi lub niepoprawnymi zmianami (mówimy tu o hermetyzacji lub enkapsulacji). Metody różnych klas mogą nosić takie same nazwy, co pozwala na pisanie bardziej intuicyjnego i zrozumiałego kodu.

4.2. Dziedziczenie

Na tym nie kończą się zalety programowania obiektowego. Kolejną wygodną dla programisty cechą jest dziedziczenie (ang. inheritance). Pozwala ono na dostosowywanie istniejących klas do własnych potrzeb poprzez wzbogacanie ich o dodatkowe pola lub metody. Tak utworzona klasa może przy tym korzystać ze wszystkich nieprywatnych pól i metod swojego „przodka”.

Dziedziczenie to wykorzystanie istniejących klas do tworzenia nowych, wykorzystujących pola i metody „przodka”.

Przykład 7. Wyjaśnienie dziedziczenia

W przykładzie 1. zdefiniowaliśmy klasę Człowiek. Uczeń jest „szczególnym przypadkiem” człowieka (ma te same cechy i potrafi wykonywać te same czynności), jednak ma też swoje specyficzne właściwości.

Klasa Uczeń jest oparta na klasie Człowiek.
Klasa Uczeń posiada te same pola, co klasa Człowiek, oraz dodatkowo pola:
    szkoła
    klasa
    lista ocen
    …
Obiekt klasy Uczeń potrafi wykonywać te same czynności, co obiekt klasy Człowiek, oraz dodatkowo czynności:
    uczyć się
    odpowiadać
    pisać sprawdziany
    …

Klasę opartą na innej klasie nazywamy klasą pochodną. Klasę, po której następuje dziedziczenie, nazywamy klasą bazową.

Ćwiczenie 10. Stosujemy dziedziczenie

Zastanów się, jakie inne klasy można utworzyć na bazie klasy Człowiek. Rozważ przypadek klasy Uczeń liceum ogólnokształcącego. Jaka klasa będzie przodkiem? Jakie dodatkowe metody powinna posiadać nowa klasa?

Ćwiczenie 11. Rysujemy schemat blokowy struktury klas

Narysuj w postaci schematu strukturę klas rozważanych w ćwiczeniu 10. Jakiego rodzaju strukturę otrzymaliśmy?


Dziedziczenie klas powoduje powstanie swego rodzaju drzewa. Pojawiające się w nim klasy tworzą hierarchię klas. Odpowiednie zaprojektowanie struktury klas pozwala na skrócenie kodu – kod wspólny dla kilku klas umieszczamy we wspólnej klasie bazowej. Można to zauważyć, porównując:

  1. C++ w języku C++ kod programu z przykładu 2. i ćwiczenia 3., w którym pewne metody klas CProstokat i COkrag są identyczne,
  2. Python w języku Python kod programu z przykładu 3. i ćwiczenia 7., w którym pewne metody klas Prostokat i Okrag są identyczne.
C++

W języku C++ po nazwie klasy, po dwukropku podajemy nazwę klasy bazowej, poprzedzając ją ewentualnie modyfikatorem widoczności (public– oznacza, że dziedziczone elementy mają taką samą widoczność, jak w klasie bazowej; z pozostałymi modyfikatorami widoczności można zapoznać się w literaturze dodatkowej).

class CKlasaPochodna : public CKlasaBazowa
{
public:
    typ1 PoleDodatkowe1;
    typ2 PoleDodatkowe2;
// ...
    void MetodaDodatkowa1();
}

W języku Python po nazwie klasy dodajemy nazwę klasy bazowej w nawiasie okrągłym.

class KlasaPochodna(KlasaBazowa):
    def dodatkowa_metoda(self):
        ...

4.3. Polimorfizm

Polimorfizm to możliwość zdefiniowania w klasach pochodnych metod o takich samych nazwach, jak w klasie bazowej, lecz wykonujących różne czynności.
Metoda wirtualna to metoda powiązana z obiektem dynamicznie (w języku C++ kompilator wybiera stosowną metodę na podstawie klasy obiektu).
Metody wirtualne pozwalają na korzystanie z polimorfizmu.

C++
W języku C++ z polimorfizmu korzysta się, deklarując odpowiednie metody jako metody wirtualne. Oznacza się je słowem kluczowym virtual, które musi znajdować się przed deklaracją typu metody.


W języku Python każda zdefiniowana metoda jest wirtualna.

Wykorzystanie polimorfizmu jest zagadnieniem złożonym, dlatego w ramach tego materiału nie będziemy go omawiać.

4.4. Metody statyczne

Metody w programowaniu obiektowym operują na konkretnych danych, należących do obiektu (pól). Czasami zachodzi jednak potrzeba zdefiniowania metody logicznie przynależącej do danej klasy, natomiast niewykorzystującej konkretnych danych.

Przykład 8. Tworzenie klasy do obsługi dat

Utworzymy klasę CData do obsługi dat:

C++
class CData
{
public:
     int dzien;
     int miesiac;
     int rok;
     CData(int d, int m, int r);      // Konstruktor
     string DataTekstowo();
     int DzienRoku();
     static bool CzyRokPrzestepny(int rok);
};

Utworzymy klasę Data do obsługi dat:

class Data:
    def __init__(self, d, m, r):
        self.dzien = d
        self.miesiac = m
        self.rok = r
    def data_tekstowo(self):
        ...
    def dzien_roku(self):
        ...
    @staticmethod
    def czy_rok_przestepny(rok):
        ... 
C++

Klasa posiada pola dzien, miesiac, rok – do przechowywania elementów daty, konstruktor, tworzący obiekt odpowiadający określonemu rokowi, miesiącowi i dniu, oraz metody:
   – DataTekstowo(), zwracającą daną datę w postaci tekstu,
   – DzienRoku(), obliczająca i zwracającą, którym dniem roku jest dzień odpowiadający dacie.
Implementacja metod (plik TC4_p5_Daty.cpp) odwołuje się do pól dzien, miesiac i rok. Aby jednak obliczyć numer dnia w roku, musimy sprawdzić, ile dni miał luty tego roku (w latach zwykłych ma 28 dni, w latach przestępnych – 29). Sprawdzenie przestępności roku jest czynnością logicznie powiązaną z datami, natomiast niepowiązaną z konkretnym obiektem (do metody CzyRokPrzestepny() przekazujemy parametr rok). Dlatego też metodę CzyRokPrzestepny() zadeklarujemy jako statyczną.

Metody statyczne wywołujemy, poprzedzając nazwę metody nazwą klasy, np.:

cout << 2000 << " " << CData::CzyRokPrzestepny(2000) << endl;

Klasa posiada pola dzien, miesiac, rok – do przechowywania elementów daty, konstruktor, tworzący obiekt odpowiadający określonemu rokowi, miesiącowi i dniu, oraz metody:
   – data_tekstowo(), zwracającą daną datę w postaci tekstu,
   – dzień_roku(), obliczająca i zwracającą, którym dniem roku jest dzień odpowiadający dacie.
Implementacja metod (plik TC4_p5_Daty.py) odwołuje się do pól dzien, miesiac i rok. Aby jednak obliczyć numer dnia w roku, musimy sprawdzić, ile dni miał luty tego roku (w latach zwykłych ma 28 dni, w latach przestępnych – 29). Sprawdzenie przestępności roku jest czynnością logicznie powiązaną z datami, natomiast niepowiązaną z konkretnym obiektem (do metody czy_rok_przestepny() przekazujemy parametr rok). Dlatego też metodę czy_rok_przestepny() zadeklarujemy jako statyczną.

Metody statyczne wywołujemy, poprzedzając nazwę metody nazwą klasy, np.:

print(f'rok 2000: przestępny: {Data.czy_rok_przestepny(2000)}')

Zadania

Uwagi:

  • Aby wykonywać zadania 1-4, otwórz plik TC4_p5_Daty.cpp lub TC4_p5_Daty.py
  • Jeśli nie ma oznaczenia „C++” lub „Python”, zadanie można zrobić w języku C++  lub w języku Python.
  1. Uzupełnij klasę CData (C++) lub Data (Python) o metodę obliczającą, ile dni minęło między 1 stycznia 1900 roku a daną datą.
  2. Uzupełnij klasę CData lub Data (Python) o metodę obliczającą liczbę dni pomiędzy dwiema datami. Skorzystaj z metody utworzonej w zadaniu 1.
  3. Uzupełnij klasę:
    C++ CData o metody ZmienNaJutro(), ZmienNaWczoraj() przesuwające datę o jeden dzień do przodu lub do tyłu.
    Python Data o metody zmien_na_jutro(), zmien_na_wczoraj() przesuwające datę o jeden dzień do przodu lub do tyłu.
  4. Uzupełnij klasę:
    C++ CData o metodę umożliwiającą zmianę daty o zadaną liczbę dni. Wykorzystaj metody ZmienNaJutro(), ZmienNaWczoraj().
    Python Data o metodę umożliwiającą zmianę daty o zadaną liczbę dni. Wykorzystaj metody zmien_na_jutro(), zmien_na_wczoraj().

  5. Dla zainteresowanych
  6. Znajdź informacje o przeciążaniu operatorów. Skorzystaj z możliwości przeciążania operatorów do przeciążenia operatora minus (-) w celu obliczenia różnicy między dwiema datami.
  7. C++ Znajdź informacje na temat funkcji sprintf() (i pokrewnych: printf(), fprintf()), wykorzystanej w implementacji metody CData::DataTekstowo(). Wymień przykładowe elementy formatowania, z jakich można korzystać w tych funkcjach.
  8. Python Znajdź informacje na temat użytych w plikach ćwiczeń f-ciągów (ang. f-string), czyli sposobu formatowania danych wyjściowych w języku Python. Spróbuj tak zmienić klasę Data, aby miesiąc i dzień zawsze używały dwóch znaków, np. pierwszy stycznia 2022 jako 01.01.2022.