Menu
Coddy logo textTech

Zasada trzech / pięciu / zera

Część sekcji Programowanie obiektowe ścieżki C++ w Coddy. Lekcja 25 z 104.

Gdy Twoja klasa zarządza zasobami, takimi jak pamięć dynamiczna, wiesz już, że potrzebujesz własnego destruktora, konstruktora kopiującego i konstruktora przenoszącego. Istnieje jednak zasada, która pomaga zdecydować, które specjalne funkcje składowe zaimplementować: reguła trzech, pięciu i zera.

Reguła trzech mówi: jeśli zdefiniujesz którykolwiek z tych trzech elementów, powinieneś zdefiniować wszystkie trzy:

  • Destruktor
  • Konstruktor kopiujący
  • Operator przypisania kopiującego

Reguła pięciu rozszerza to podejście we współczesnym C++, dodając operacje przenoszenia:

  • Destruktor
  • Konstruktor kopiujący
  • Operator przypisania kopiującego
  • Konstruktor przenoszący
  • Operator przypisania przenoszącego
class Buffer {
    int* data;
    size_t size;
public:
    Buffer(size_t s) : size(s), data(new int[s]) {}
    
    ~Buffer() { delete[] data; }                          // 1. Destruktor
    Buffer(const Buffer& other);                          // 2. Konstruktor kopiujący
    Buffer& operator=(const Buffer& other);               // 3. Przypisanie kopiujące
    Buffer(Buffer&& other) noexcept;                      // 4. Konstruktor przenoszący
    Buffer& operator=(Buffer&& other) noexcept;           // 5. Przypisanie przenoszące
};

Reguła zera to najprostsze podejście: jeśli Twoja klasa nie zarządza bezpośrednio zasobami, nie definiuj żadnej z tych funkcji. Pozwól kompilatorowi je wygenerować albo użyj inteligentnych wskaźników i standardowych kontenerów, które zajmą się zasobami za Ciebie.

class Player {
    std::string name;              // std::string samodzielnie zarządza swoją pamięcią
    std::vector<int> scores;       // std::vector samodzielnie zarządza swoimi zasobami
public:
    Player(std::string n) : name(n) {}
    // Nie potrzeba destruktora ani funkcji kopiujących czy przenoszących!
};

Przestrzeganie tych zasad zapobiega błędom, takim jak podwójne usuwanie, wycieki pamięci i wiszące wskaźniki, które występują, gdy niektóre operacje są zdefiniowane, a innych brakuje.

challenge icon

Wyzwanie

Łatwy

Stwórzmy klasę TextBuffer zgodną z Regułą Pięciu: zaimplementujemy wszystkie pięć specjalnych funkcji składowych, aby prawidłowo zarządzać dynamicznie przydzielanymi danymi znakowymi. Pokaże to, jak operacje kopiowania i przenoszenia współpracują ze sobą, tworząc solidną klasę zarządzającą zasobami.

Utworzysz dwa pliki, aby uporządkować swój kod:

  • TextBuffer.h: Zdefiniuj klasę TextBuffer, która przechowuje tekst w dynamicznie przydzielanej tablicy znaków. Twoja klasa powinna zawierać:
    • Prywatne składowe: wskaźnik char* o nazwie data przechowujący tekst oraz size_t length przechowujący długość ciągu znaków (bez znaku terminującego null)
    • Konstruktor parametryzowany, który przyjmuje ciąg znaków C (const char*), przydziela pamięć, kopiuje zawartość i wypisuje "TextBuffer created: <text>"
    • Destruktor, który zwalnia pamięć (jeśli nie jest null) i wypisuje "TextBuffer destroyed"
    • Konstruktor kopiujący, który wykonuje kopię głęboką i wypisuje "TextBuffer copied"
    • Operator przypisania kopiującego, który obsługuje przypisanie do samego siebie, zwalnia istniejące dane, wykonuje kopię głęboką i wypisuje "TextBuffer copy-assigned". Zwróć *this
    • Konstruktor przenoszący (oznaczony jako noexcept), który przenosi własność i wypisuje "TextBuffer moved". Pozostaw obiekt źródłowy w prawidłowym, pustym stanie
    • Operator przypisania przenoszącego (oznaczony jako noexcept), który obsługuje przypisanie do samego siebie, zwalnia istniejące dane, przenosi własność i wypisuje "TextBuffer move-assigned". Zwróć *this
    • Metodę getText(), która zwraca przechowywany tekst (zwróć pusty ciąg "", jeśli data jest null)
    • Metodę getLength(), która zwraca długość
  • main.cpp: Pokaż działanie wszystkich pięciu specjalnych funkcji składowych. Odczytaj ciąg tekstowy z wejścia, a następnie:
    • Utwórz obiekt TextBuffer o nazwie original z tekstem wejściowym
    • Utwórz obiekt copied za pomocą konstruktora kopiującego, kopiując obiekt original
    • Utwórz obiekt another z tekstem "Temporary"
    • Użyj przypisania kopiującego: another = original
    • Utwórz obiekt moved, używając konstruktora przenoszącego do przeniesienia obiektu original za pomocą std::move()
    • Utwórz obiekt target z tekstem "Target"
    • Użyj przypisania przenoszącego: target = std::move(copied)
    • Wypisz "--- Final State ---"
    • Dla każdego bufora wypisz "original: <text> (length: <len>)": original, copied, moved, another, target

Po przeniesieniu obiekty źródłowe (original i copied) powinny zawierać pusty tekst o długości 0, a obiekty docelowe — przeniesione dane. To pokazuje Regułę Pięciu w działaniu: wszystkie pięć funkcji współpracuje, aby zapewnić bezpieczne zarządzanie zasobami.

Dołącz <cstring>, aby korzystać z funkcji do obsługi ciągów znaków, takich jak strlen i strcpy, oraz <utility>, aby korzystać z std::move().

Spróbuj swoich sił

#include <iostream>
#include <string>
#include <utility>
#include "TextBuffer.h"

using namespace std;

int main() {
    string input;
    getline(cin, input);

    // TODO: Utwórz obiekt TextBuffer o nazwie 'original' z tekstem wejściowym

    // TODO: Utwórz 'copied' za pomocą konstruktora kopiującego, używając 'original'

    // TODO: Utwórz 'another' z tekstem "Temporary"

    // TODO: Użyj przypisania kopiującego: another = original

    // TODO: Utwórz 'moved' za pomocą konstruktora przenoszącego, używając 'original' i std::move()

    // TODO: Utwórz 'target' z tekstem "Target"

    // TODO: Użyj przypisania przenoszącego: target = std::move(copied)

    // TODO: Wypisz "--- Final State ---"

    // TODO: Wypisz stan każdego bufora w tym formacie:
    // "original: <text> (length: <len>)"
    // Wypisz dla: original, copied, moved, another, target

    return 0;
}
quiz iconSprawdź się

Ta lekcja zawiera krótki quiz. Zacznij lekcję, żeby na niego odpowiedzieć i śledzić swoje postępy.

Wszystkie lekcje w sekcji Programowanie obiektowe

Poćwicz samodzielnie: Kompilator C++ online