Přeskočit na obsah

Třídy, struktury a zapouzdření

Understand the difference between struct and class, access specifiers, encapsulation, const member functions, static members, and modern aggregate initialization.

Co je třída?

třída v C++ je uživatelem definovaný typ, který sdružuje data (členské proměnné) a operace s těmito daty (členské funkce) do jediné jednotky. Toto spojení se nazývá zapouzdření — jeden ze čtyř pilířů OOP.

Klíčovým poznatkem je, že třída definuje invariant: sadu pravidel, která musí být vždy pravdivá o jejím vnitřním stavu. Například třídaDatemůže vyžadovat, aby byl měsíc vždy mezi 1 a 12. Zapouzdření umožňuje vynutit tyto invarianty řízením přístupu k vnitřnímu stavu.

struct vs class

In C++,structandclassare almost identical. The only difference is the default access specifier:structdefaults topublic, whileclassdefaults toprivate. Convention: usestructfor plain data aggregates,classwhen you have invariants to maintain.

struct_vs_class.cpp
#include <iostream>
#include <string>

// struct: members are public by default
struct Point {
    double x;  // public
    double y;  // public
};

// class: members are private by default
class BankAccount {
    std::string owner;   // private
    double balance;      // private

public:
    BankAccount(const std::string& name, double initial)
        : owner{name}, balance{initial} {}

    double get_balance() const { return balance; }

    void deposit(double amount) {
        if (amount > 0) {
            balance += amount;
        }
    }
};

int main() {
    Point p{3.0, 4.0};  // aggregate initialization
    std::cout << p.x << ", " << p.y << '\n';

    BankAccount acct{"Alice", 1000.0};
    acct.deposit(500.0);
    std::cout << acct.get_balance() << '\n';  // 1500
    // acct.balance = -999;  // ERROR: private member

    return 0;
}

Access Specifiers & the this Pointer

C++ poskytuje tři úrovně přístupu: public (přístup má kdokoli), private (pouze třída a její přátelé) a protected (třída, její přátelé a odvozené třídy). Ukazatelthisje implicitní ukazatel na aktuální objekt dostupný v každé nestatické členské funkci.

access_specifiers.cpp
#include <iostream>
#include <string>

class Employee {
private:
    std::string name_;
    int id_;

protected:
    double salary_;  // accessible to derived classes

public:
    Employee(const std::string& name, int id, double salary)
        : name_{name}, id_{id}, salary_{salary} {}

    // const member function: promises not to modify the object
    const std::string& get_name() const { return name_; }
    int get_id() const { return id_; }

    // 'this' pointer usage — return *this for method chaining
    Employee& set_name(const std::string& name) {
        this->name_ = name;  // 'this->' disambiguates
        return *this;
    }

    Employee& set_id(int id) {
        this->id_ = id;
        return *this;
    }

    void print() const {
        std::cout << "Employee{" << name_ << ", #" << id_
                  << ", $" << salary_ << "}\n";
    }
};

int main() {
    Employee e{"Bob", 42, 75000.0};
    e.set_name("Robert").set_id(43);  // method chaining via *this
    e.print();  // Employee{Robert, #43, $75000}
    return 0;
}

Static Members & Friend

Statické členy patří do třídy, nikoli do žádné instance. Použijte je pro sdílené stavové nebo pomocné funkce. Klíčové slovo friend uděluje soukromým členům přístup k nečlenské funkci nebo jiné třídě – používejte jej střídmě, protože narušuje zapouzdření.

static_friend.cpp
#include <iostream>

class Widget {
    int value_;

    // C++17: inline static — define in header, no .cpp needed
    inline static int count_ = 0;

public:
    explicit Widget(int v) : value_{v} { ++count_; }
    ~Widget() { --count_; }

    static int get_count() { return count_; }

    // friend function can access private members
    friend std::ostream& operator<<(std::ostream& os, const Widget& w);
};

std::ostream& operator<<(std::ostream& os, const Widget& w) {
    return os << "Widget(" << w.value_ << ')';  // accesses private value_
}

int main() {
    Widget a{10}, b{20};
    std::cout << Widget::get_count() << '\n';  // 2
    std::cout << a << '\n';  // Widget(10)
    {
        Widget c{30};
        std::cout << Widget::get_count() << '\n';  // 3
    }
    std::cout << Widget::get_count() << '\n';  // 2
    return 0;
}

Agregovaná inicializace a určené inicializátory (C++20)

agregát je třída nebo struktura bez uživatelem deklarovaných konstruktorů, bez soukromých/chráněných nestatických datových členů, bez virtuálních funkcí a bez virtuálních základních tříd. C++20 přidává určené inicializátory, které vám umožní pojmenovat, které členy inicializujete.

designated_init.cpp
#include <iostream>
#include <string>

struct Config {
    std::string host = "localhost";
    int port = 8080;
    bool use_tls = false;
    int max_connections = 100;
};

int main() {
    // Traditional aggregate initialization
    Config c1{"example.com", 443, true, 200};

    // C++20 designated initializers — much more readable
    Config c2{
        .host = "api.example.com",
        .port = 443,
        .use_tls = true
        // .max_connections uses default value 100
    };

    std::cout << c2.host << ':' << c2.port << '\n';
    std::cout << "TLS: " << std::boolalpha << c2.use_tls << '\n';
    std::cout << "Max: " << c2.max_connections << '\n';  // 100
    return 0;
}
Osvědčený postup

Ve výchozím nastavení nastavte datové členy na soukromé a zpřístupněte pouze to, co je potřeba, prostřednictvím minimálního veřejného rozhraní. Pokud třída nemá žádný invariant k údržbě (je to jen svazek dat), použijte místo toho struct s veřejnými členy. Nepřidávejte getry a settery pro každé pole – to maří účel zapouzdření. Místo toho poskytněte smysluplné operace jakodeposit()awithdraw()spíše nežset_balance().

Úskalí

Pokud členská funkce nemění stav objektu, označte jejconst. Bezconstnemůžete volat funkci na objektuconstnebo prostřednictvím odkazuconst— což se stává neustále při předávání objektů pomocíconst&. Chybějícíconstna přístupovém objektu je běžným zdrojem chyb při kompilaci ve větších kódových základnách.

Klíčové poznatky
  • structaclassse liší pouze výchozím přístupem:structje veřejný,classje soukromý
  • Zapouzdření chrání invarianty třídy – používejte dataprivatese smysluplným veřejným rozhraním
  • Označte nemutující členské funkceconst, aby fungovaly s odkazyconst
  • Pro statické členy definované v záhlaví použijteinline static(C++17).
  • Inicializátory určené pro C++20 činí agregovanou inicializaci čitelnou a samodokumentující
  • friendby měl být používán střídmě – obvykle pro přetížení operátorů, kteří potřebují soukromý přístup

Kvíz — Otestujte své znalosti

(15 XP)

1. Jaký je JEDINÝ rozdíl mezi`struct`a`class`v C++?

2. Proč byste měli označit členské funkce přístupového objektu jako`const`?

3. What does`inline static int count_ = 0;`achieve in C++17?