Menu

Klasy w C++: obiekty, pola i metody

Jak klasy w C++ łączą dane i zachowanie w typy wielokrotnego użytku: deklarowanie pól i metod, tworzenie obiektów, podział na public i private oraz pułapki, takie jak niezainicjalizowane pola i wskaźnik this.

Na tej stronie są działające edytory: edytuj, uruchamiaj i od razu zobacz wynik.

Czym jest klasa

Klasa to plan własnego typu, który łączy dane (pola, czyli zmienne składowe) i zachowanie (metody, czyli funkcje składowe) w jedną całość. Wbudowane typy, takie jak int i double, opisują pojedyncze wartości, a klasa pozwala zamodelować całe pojęcie (konto bankowe, punkt 2D, gracza) jako jedną wartość, którą możesz przekazywać dalej.

Z klas korzystasz już, choć żadnej jeszcze nie zdefiniowano: std::string i kontener vector to klasy z biblioteki standardowej. Wywołanie name.length() albo v.push_back(3) to wywołanie metody na obiekcie. Teraz zbudujesz w ten sam sposób własne typy, a potem przejdziesz do konstruktorów, które je inicjalizują.

Definiowanie klasy i tworzenie obiektów

Definicja klasy wymienia składowe w klamrach i kończy się średnikiem. Zapomniany końcowy ; to jeden z najczęstszych błędów początkujących. Każdy obiekt utworzony z klasy dostaje własną kopię pól.

rex i luna to dwa osobne obiekty. Zmiana rex.name nie dotyka luna.name: każdy obiekt przechowuje własne dane. Do składowej sięgasz operatorem kropki . na obiekcie (albo ->, gdy masz wskaźnik na obiekt).

public kontra private

Domyślnie wszystko w class jest prywatne: dotykać tego mogą tylko metody tej klasy. Etykieta public: otwiera składowe dla kodu z zewnątrz. Ten podział to serce enkapsulacji: udostępniasz bezpieczny interfejs i ukrywasz wewnętrzne dane, aby nie dało się ich doprowadzić do niepoprawnego stanu.

Ponieważ count jest prywatne, żaden wywołujący nie przemyci ujemnej ani bezsensownej wartości: musi przejść przez increment(). const po liście parametrów value() obiecuje, że funkcja nie zmieni obiektu, dzięki czemu możesz ją wywołać na obiektach const, a czytelnik od razu widzi intencję.

Deklaracja a definicja metod

W małych klasach definiowanie metod w miejscu (jak wyżej) jest w porządku. W większych często deklaruje się metody wewnątrz klasy, a ich ciała definiuje na zewnątrz, używając składni zasięgu ClassName::method. Dzięki temu definicja klasy pozostaje czytelnym podsumowaniem interfejsu.

Przedrostek Rectangle:: mówi kompilatorowi, do której klasy należy funkcja. Wewnątrz takiej definicji nadal odwołujesz się do składowych samymi nazwami (width, area()), a kompilator wie, że należą do obiektu, na którym wywołano metodę.

Wskaźnik this

W każdej niestatycznej metodzie this to wskaźnik na obiekt, na którym ją wywołano. Zwykle nie jest potrzebny, bo sama nazwa składowej już oznacza bieżący obiekt. Przydaje się, gdy parametr przesłania składową (mają tę samą nazwę) i trzeba je rozróżnić.

Bez this-> zapis x = x; wewnątrz setX przypisałby parametr samemu sobie, a pole zostałoby nietknięte: cichy błąd. this->x usuwa niejednoznaczność. Wiele projektów całkiem omija ten problem, nazywając parametry inaczej (np. setX(int newX)), ale this-> będziesz w prawdziwym kodzie widzieć bez przerwy.

Częste błędy

Kilka pułapek związanych z klasami łapie ludzi raz za razem:

  • Brak średnika na końcu. Definicja klasy kończy się };. Pomiń ;, a dostaniesz lawinę mylących błędów wskazujących na to, co jest po klasie, a nie na samą klasę.
  • Zapominanie o inicjalizacji pól. Wbudowane pola, takie jak int i double, nie są automatycznie zerowane. Odczyt niezainicjalizowanego pola to niezdefiniowane zachowanie. Nadaj polom wartości domyślne (int count = 0;) albo zainicjalizuj je w konstruktorze.
  • Dostęp do prywatnych składowych z zewnątrz. c.count = 5; na prywatnym polu to błąd kompilacji: użyj publicznej metody. To enkapsulacja działająca tak, jak powinna.
  • Mylenie klasy z obiektem. Dog.bark(); jest błędne, bo Dog to typ. Metody wywołujesz na obiektach: rex.bark();.
// Niezainicjalizowane pole: odczyt 'age' to niezdefiniowane zachowanie:
class Cat {
public:
    int age;            // brak wartości domyślnej
};

Cat c;
std::cout << c.age;     // śmieciowa wartość, nie 0

Dalej: konstruktory

Ustawianie każdego pola ręcznie po utworzeniu obiektu (rex.name = ...; rex.age = ...;) jest żmudne i łatwo o nim zapomnieć, a tak właśnie powstają niezainicjalizowane pola. Następna strona omawia konstruktory: specjalne funkcje, które uruchamiają się automatycznie przy tworzeniu obiektu i gwarantują, że każdy obiekt startuje w poprawnym stanie, z czystą, jednolinijkową składnią Dog rex("Rex", 4);.

Najczęściej zadawane pytania

Jaka jest różnica między klasą a obiektem w C++?

Klasa to plan: opisuje, jakie dane (pola) i zachowanie (metody) ma typ. Obiekt to konkretna instancja zbudowana według tego planu. class Dog { ... }; definiuje typ raz; Dog rex; tworzy prawdziwego Dog, z którego możesz korzystać. Jedna klasa może dać wiele niezależnych obiektów.

Jaka jest różnica między class a struct w C++?

Technicznie tylko domyślny poziom dostępu: składowe class są domyślnie private, a składowe struct domyślnie public. Obie mogą mieć metody, konstruktory i dziedziczenie. Zgodnie z konwencją struct służy do prostych paczek danych, a class do typów z zachowaniem i niezmiennikami do ochrony.

Do czego służy wskaźnik this w klasie C++?

Wewnątrz metody this to wskaźnik na obiekt, na którym ją wywołano. Używasz go, aby odróżnić parametr od pola (this->x = x;) albo zwrócić bieżący obiekt. Przy zwykłym dostępie do składowych rzadko jest potrzebny: samo x już oznacza x bieżącego obiektu.

Ilustracja języków programowania w Coddy

Ucz się programowania z Coddy

ZACZNIJ