Programowanie STM32 w C++ – struktury i klasy

Język C++ to jeden z najpopularniejszych jęków programowania, który oprócz szerokich możliwości operowania na danych, pozwala na wykorzystanie zaawansowanych mechanizmów programowania obiektowego. W poprzednim artykule „Programowanie STM32 w C++ – pierwszy projekt” przedstawiłem sposób konfiguracji projektu dla STM32 z wykorzystaniem języka C++ w środowisku STM32CubeIDE oraz prosty przykład do sterowania LED przy pomocy klasy. W tym artykule przyjrzymy się dokładniej podstawowym elementom C++, jakimi są struktury i klasy. Zastanowimy się także, jakie są różnice między strukturą w C a strukturą w C++ oraz jak struktury w C++ różnią się od klas.

Struktura w języku C

Przypomnijmy sobie na początek podstawowe informacje na temat struktur w języku C. Struktura jest zbiorem różnych typów danych, które są przechowywane w jednym bloku pamięci. Każdy element w strukturze, nazywany składową (ang. member), może być różnego typu. W C struktura jest używana przede wszystkim do grupowania danych, a nie do implementowania funkcjonalności, ponieważ C nie jest językiem obiektowym.

Przykład struktury w języku C:

struct Point {
    int x;
    int y;
};

void Point_Init(struct Point *point, int x, int y)
{
    point->x = x;
    point->y = y;
}

W tym przykładzie mamy do czynienia ze strukturą Point, która przechowuje dwie zmienne typu int. Struktury w C pozwalają jedynie na przechowywanie danych – nie oferują metod, które operowałyby na tych danych. Żeby stworzyć namiastkę konstruktora, musimy zdefiniować go poza strukturą.

Jak użyć struktury z takim „inicjalizatorem”? Musimy zadeklarować zmienną i przekazać ją wraz z argumentami. Przy deklaracji koniecznie dodajemy słowo kluczowe „struct”, jeżeli nie zastosowaliśmy żadnego typdef-a.

struct Point p1;
Point_Init(&p1, 2, 3);

Często deklaruje się dodatkowo „typedef”, żeby uniknąć pisania słowa „struct”.

struct Point {
    int x;
    int y;
};

typedef struct Point sPoint;

Lub

typedef struct Point {
    int x;
    int y;
} sPoint;

Tutaj deklaracja nie wymaga użycia słowa struct. Zamiast „tego „struct Point” używamy nowego typu sPoint.

sPoint p1;
Point_Init(&p1, 2, 3);

Oczywiście w tym przypadku pola struktury możemy zainicjalizować również przy deklaracji używając nawiasu klamrowego. Jednak jeśli chcemy wykonać jednocześnie jakieś dodatkowe operacje, musimy zrobić to za pomocą inicjalizatora.

Struktury w języku C umożliwiają przechowywanie wielu zmiennych o różnych typach pod jedną nazwą, co ułatwia zarządzanie danymi. Dzięki strukturze, programista może zdefiniować złożony obiekt, który składa się z różnych pól, takich jak liczby, napisy czy wskaźniki. Struktury są wykorzystywane w wielu dziedzinach, takich jak zarządzanie danymi, obsługa baz danych czy tworzenie bardziej zaawansowanych algorytmów. Struktury są także wykorzystywane w interfejsach systemowych oraz przy tworzeniu bibliotek i modułów, które wymagają przechowywania wielu powiązanych danych.

Struktura w języku C++

Język C++ rozszerza możliwości struktur, pozwalając na dodanie funkcji (metod), konstruktora, destruktora, a także na korzystanie z modyfikatorów dostępu (public, private, protected). Dzięki temu struktura w C++ może pełnić rolę klasy, umożliwiając programiście tworzenie obiektów o większych możliwościach niż w języku C. Konstruktor pozwala na inicjalizację danych obiektu przy jego tworzeniu, natomiast destruktor umożliwia zwolnienie zasobów w momencie niszczenia obiektu. Modyfikatory dostępu, takie jak public, private i protected, kontrolują widoczność i dostępność poszczególnych elementów struktury, co poprawia bezpieczeństwo i organizację kodu.

Zamiast tylko przechowywać dane, struktury w C++ mogą teraz posiadać metody, które operują na tych danych, umożliwiając realizację bardziej złożonych operacji. Dzięki temu C++ wprowadza pełną enkapsulację, umożliwiając tworzenie bardziej modularnych i łatwiejszych do utrzymania programów. Struktury w C++ stają się więc podstawą obiektowego podejścia do programowania, łącząc dane i funkcjonalność w jeden spójny element. Taka elastyczność sprawia, że struktury w C++ mogą być wykorzystywane w szerokim zakresie zastosowań, od prostych programów po skomplikowane systemy.

Przykład struktury w języku C++:

struct Point {
    int x, y;

    // Konstruktor
    Point(int x_val, int y_val) : x(x_val), y(y_val) {}

    // Metoda
    void display() const {
        // implementacja
    }
};

Struktury w języku C++ używamy bez słowa kluczowego „struct”. Przy deklaracji możemy jednocześnie zainicjalizować dane za pomocą konstruktora.

Point p1(10, 20);

W tym przypadku mamy do czynienia ze strukturą Point, która zawiera zarówno zmienne (x i y), jak i metodę display(), która wyświetla współrzędne punktu. Co istotne, konstruktor jest również zdefiniowany w obrębie struktury, co w C++ jest standardową praktyką, a struktura może mieć dostęp do funkcji z modyfikatorami dostępu.

Różnice między strukturą w C a strukturą w C++

Najważniejszą różnicą pomiędzy strukturą w C a strukturą w C++ jest to, że w C++ struktura może mieć zarówno funkcje, jak i modyfikatory dostępu, co sprawia, że staje się bardziej obiektowa. W C struktury są jedynie zbiorem danych, które nie mają związanych z nimi funkcji, co ogranicza ich możliwości w porównaniu do C++.

C++ pozwala na pełne wykorzystanie struktur jako obiektów, które mogą mieć metody, konstruktor i destruktor, co umożliwia lepsze zarządzanie pamięcią i innymi zasobami. Dodatkowo, struktury w C++ mogą korzystać z modyfikatorów dostępu, takich jak public, private i protected, co pozwala na kontrolowanie widoczności i dostępu do poszczególnych członów struktury. Tego typu funkcjonalności pozwalają na tworzenie bardziej elastycznych i bezpiecznych struktur danych, ponieważ programista może ograniczyć dostęp do wewnętrznych szczegółów implementacji i umożliwić modyfikację tylko określonych elementów. Konstruktor pozwala na automatyczną inicjalizację danych struktury, co upraszcza kod i zmniejsza ryzyko błędów. Z kolei destruktor jest przydatny do zwalniania zasobów, takich jak pamięć dynamiczna, co zapobiega wyciekom pamięci.

Struktury w C++ mogą również dziedziczyć po innych strukturach, co pozwala na wykorzystanie mechanizmów dziedziczenia i tworzenie bardziej złożonych hierarchii obiektów. Dodatkowo, dzięki metodom, struktury w C++ mogą zawierać logikę operującą na przechowywanych danych, co czyni je bardziej wszechstronnymi i potężnymi narzędziami. W rezultacie, struktury w C++ umożliwiają tworzenie bardziej zaawansowanych programów, które lepiej odpowiadają na potrzeby nowoczesnego programowania obiektowego. Podsumowanie zebrałem w postaci tabeli.

Język CJęzyk C++
Przechowywanie zbioru danychPrzechowywanie danych oraz operowanie na nich
Brak możliwości dodawania funkcjiStruktury mogą zawierać funkcje (metody)
Brak modyfikatorów dostępuModyfikatory public, private, protected
Deklaracja przy użyciu słowa kluczowego „struct”Deklaracja bez słowa kluczowego „struct” (tylko nazwa struktury)
Brak konstruktora i destruktoraKonstruktor do inicjalizacji danych i destruktor do zwalniania zasobów
Brak specjalnych mechanizmów zarządzania pamięciąKonstruktor i destruktor pozwalają na lepsze zarządzanie pamięcią
Brak dziedziczeniaStruktury mogą dziedziczyć po innych strukturach
Publiczny dostęp do danychMożliwość kontrolowania dostępu do danych przez modyfikatory

Struktura i klasa w C++

W C++ klasy i struktury są bardzo podobne, ponieważ oba te mechanizmy służą do tworzenia własnych typów danych. Oba typy pozwalają na przechowywanie danych (składowych) oraz implementowanie metod, konstruktorów, destruktorów itd. Główna różnica między nimi polega na domyślnym dostępie do składowych: w przypadku klas, składowe są domyślnie prywatne (private), co oznacza, że nie można uzyskać do nich dostępu z zewnątrz, chyba że zostaną jawnie zadeklarowane jako publiczne. Z kolei w strukturach składowe są domyślnie publiczne, co oznacza, że są dostępne bezpośrednio z zewnątrz. Ta różnica w dostępie wpływa na sposób, w jaki są wykorzystywane w praktyce – klasy są zwykle wykorzystywane do implementacji bardziej złożonych obiektów z kontrolą dostępu, podczas gdy struktury często stosuje się w przypadkach, gdzie prostota i bezpośredni dostęp do danych są priorytetowe.

Klasa w C++

class Point {
private:
    int x, y;

public:
    Point(int x_val, int y_val) : x(x_val), y(y_val) {}

    void display() const {
        // implementation
    }

    void setX(int x_val) { x = x_val; }
    void setY(int y_val) { y = y_val; }
};

W tym przykładzie Point jest klasą, która posiada prywatne składowe (x i y) oraz publiczne metody dostępu. Zauważ, że składowe klasy są domyślnie prywatne (jeśli nie zostanie określony inny modyfikator dostępu), co oznacza, że dostęp do nich możliwy jest tylko przez publiczne metody. Oznacza to, że powyższy przykład implementacji klasy jest równoznaczny z tym przedstawionym poniżej.

class Point {
    int x, y;

public:
    Point(int x_val, int y_val) : x(x_val), y(y_val) {}

    void display() const {
        // implementacja
    }

    void setX(int x_val) { x = x_val; }
    void setY(int y_val) { y = y_val; }
};

Inaczej jest w przypadku struktur. Przykład przedstawiony przeze mnie na początku artykułu należy rozumieć w ten sposób, że domyślnie pola x i y są publiczne, co oznacza, że zapis:

struct Point {
    int x, y;

    // Konstruktor
    Point(int x_val, int y_val) : x(x_val), y(y_val) {}

    // Metoda
    void display() const {
        // implementacja
    }
};

Jest równoznaczny z zapisem:

struct Point {
public:
    int x, y;

    // Konstruktor
    Point(int x_val, int y_val) : x(x_val), y(y_val) {}

    // Metoda
    void display() const {
        // implementacja
    }
};

Aby uzyskać pola prywatne, należy je wyraźnie określić:

struct Point {
private:
    int x, y;

public:
    // Konstruktor
    Point(int x_val, int y_val) : x(x_val), y(y_val) {}

    // Metoda
    void display() const {
        // implementacja
    }
};

Struktury i klasy w C++ używamy w taki sam sposób.

Point p1(10, 20);

Różnice między klasą a strukturą w C++

Podsumujmy główne różnice pomiędzy klasą a strukturą w języku C++. Domyślny modyfikator dostępu to jedna z kluczowych różnic. W przypadku struktury składowe domyślnie mają modyfikator dostępu public, co oznacza, że można do nich bezpośrednio uzyskać dostęp z zewnątrz. W klasie domyślnym modyfikatorem dostępu jest private, co sprawia, że składowe są niedostępne z zewnątrz i wymagają stosowania metod dostępu, takich jak gettery i settery. Dzięki temu klasy oferują lepszą kontrolę nad danymi i pozwalają na ich ukrycie, co jest podstawą zasady enkapsulacji w programowaniu obiektowym.

Struktura w C++ jest zwykle używana do przechowywania danych, gdzie dostęp do nich może być publiczny, a sama struktura nie musi zawierać złożonych funkcji. Z kolei klasa jest preferowana w sytuacjach, gdy chcemy ukryć szczegóły implementacji i umożliwić manipulację danymi jedynie za pośrednictwem publicznych metod. Klasa może także implementować mechanizmy dziedziczenia i polimorfizmu, co czyni ją bardziej odpowiednią do tworzenia bardziej złożonych aplikacji.

Zarówno struktura, jak i klasa mogą mieć funkcje członkowskie, jednak klasy mają tendencję do oferowania większej kontroli nad dostępem do swoich danych. W praktyce, struktura jest używana głównie do prostych przypadków, gdzie kontrola dostępu nie jest kluczowa, a klasa znajduje swoje zastosowanie tam, gdzie bezpieczeństwo i kontrola nad danymi mają większe znaczenie.

Klasa w C++Struktura w C++
Członkowie klasy są domyślnie prywatniCzłonkowie struktury są domyślnie publiczni
Deklaruje się ją przy użyciu słowa kluczowego classDeklaruje się ją przy użyciu słowa kluczowego struct
Zwykle używa się go do abstrakcji danych i dziedziczeniaZwykle używa się go do grupowania różnych typów danych

Podsumowanie

Choć struktury w C i C++ są bardzo podobne, to w języku C++ zyskały nowe możliwości, takie jak funkcje członkowskie, konstruktory, destruktory czy modyfikatory dostępu. Struktura w C++ jest zatem bardziej elastyczna i może pełnić rolę obiektu, który operuje na danych. Z kolei klasy w C++ oferują pełną kontrolę nad dostępem do składowych, co pozwala na lepszą enkapsulację i implementację bardziej zaawansowanych wzorców projektowych.

Klasy i struktury to podstawowe narzędzia do organizowania kodu w C++, a różnice między nimi – choć subtelne – mają istotne znaczenie w kontekście projektowania aplikacji. Wybór między klasą a strukturą zależy od kontekstu i wymagań projektu. Jeśli potrzebujemy prostego zbioru danych – struktura będzie wystarczająca, jeśli natomiast musimy kontrolować dostęp do danych i zaimplementować bardziej złożoną logikę – lepszym wyborem będzie klasa.

Dodaj komentarz

Twój adres e-mail nie zostanie opublikowany. Wymagane pola są oznaczone *