C++ Academy · Lekcja

Reguła trzech/pięciu

Poprawnie zarządzaj kopiowaniem i przenoszeniem

Lekcja 3 z 413 kroki

Reguła trzech/pięciu to bezpłatna lekcja C++ Academy na CoddyKit. To lekcja 3 z 4. Możesz przeczytać całą lekcję poniżej za darmo — a potem ćwiczyć ją interaktywnie w przeglądarce z wbudowanym edytorem kodu i tutorem AI dostępnym 24/7. To część ścieżki edukacyjnej C++ Academy, a Twój postęp synchronizuje się między webem a aplikacją CoddyKit. Kurs C++ Academy zawiera 4 lekcji w sumie.

Reguła trzech

Jeśli klasa wymaga własnego destruktora, niemal zawsze wymaga również własnego konstruktora kopiującego i operatora przypisania kopiującego. Te trzy elementy występują razem.

Dlaczego trzy

Klasa zarządzająca surowym zasobem musi określać sposób jego niszczenia, kopiowania i przypisywania — w przeciwnym razie płytkie kopie generowane przez kompilator prowadzą do podwójnego zwalniania.

#include <iostream>

class Bad {
    int* p;
public:
    Bad(int v) : p(new int(v)) {}
    ~Bad() { delete p; }
    // default copy => two objects delete the same p!
};

Niestandardowy konstruktor kopiujący

Prawidłowy konstruktor kopiujący wykonuje głęboką kopię, alokując własny zasób zamiast go współdzielić.

#include <iostream>

class Box {
    int* p;
public:
    Box(int v) : p(new int(v)) {}
    Box(const Box& o) : p(new int(*o.p)) {}   // deep copy
    ~Box() { delete p; }
    int get() const { return *p; }
};

int main() {
    Box a(5);
    Box b = a;   // independent copy
    std::cout << b.get() << "\n";
}

Niestandardowy operator przypisania kopiującego

Przypisanie kopiujące musi zwolnić stary zasób, zabezpieczyć się przed przypisaniem do samego siebie i wykonać głęboką kopię nowej wartości.

Box& operator=(const Box& o) {
    if (this != &o) {              // self-assignment guard
        delete p;
        p = new int(*o.p);
    }
    return *this;
}

Reguła pięciu

C++11 dodaje operacje przenoszenia. Jeśli zdefiniują Państwo dowolną z pięciu funkcji (destruktor, konstruktor kopiujący, przypisanie kopiujące, konstruktor przenoszący, przypisanie przenoszące), należy rozważyć zdefiniowanie wszystkich pięciu.

Konstruktor przenoszący

Konstruktor przenoszący przejmuje zasób z obiektu tymczasowego i pozostawia źródło puste — jest to znacznie tańsze niż kopiowanie.

Box(Box&& o) noexcept : p(o.p) {
    o.p = nullptr;   // source left empty
}

Przypisanie przenoszące

Przypisanie przenoszące zwalnia bieżący zasób, a następnie przejmuje własność zasobu źródłowego.

Box& operator=(Box&& o) noexcept {
    if (this != &o) {
        delete p;
        p = o.p;
        o.p = nullptr;
    }
    return *this;
}

Oznaczanie operacji przenoszenia jako noexcept

Operacje przenoszenia powinny być oznaczone jako noexcept, aby kontenery takie jak std::vector mogły bezpiecznie ich używać podczas realokacji.

Reguła zera

Najlepiej jednak nie zarządzać bezpośrednio żadnymi surowymi zasobami. Korzystając z std::unique_ptr, std::vector lub std::string, mogą Państwo nie definiować żadnej z pięciu funkcji — domyślne implementacje kompilatora są prawidłowe.

#include <memory>

class Good {
    std::unique_ptr<int> p;   // owns the resource for you
public:
    Good(int v) : p(std::make_unique<int>(v)) {}
    // no destructor, no copy/move needed
};

Usuwanie operacji

Jeśli kopiowanie nie ma sensu, należy je usunąć: Box(const Box&) = delete;. Dzięki temu typ jest celowo przenoszalny, ale niekopiowalny, albo całkowicie niekopiowalny.

Wybór właściwej reguły

Należy dążyć do stosowania reguły zera. Po regułę pięciu należy sięgać tylko wtedy, gdy rzeczywiście opakowują Państwo surowy zasób, którego nie obsługuje biblioteka standardowa.

Szybkie sprawdzenie

Proszę sprawdzić swoją znajomość reguły trzech/pięciu.

Podsumowanie

Poznali Państwo regułę trzech/pięciu: zarządzanie surowym zasobem wymaga spójnego zdefiniowania destruktora oraz operacji kopiowania i przenoszenia. Operacje przenoszenia należy oznaczać jako noexcept. Najlepiej jednak stosować regułę zera, korzystając ze standardowych typów RAII i nie definiując żadnej z tych funkcji.

Bezpłatny start

Ucz się C++ dzięki korepetycjom AI — za darmo

Pisz i uruchamiaj kod w przeglądarce, otrzymuj natychmiastową pomoc od korepetytora AI dostępnego 24/7 i kontynuuj naukę w sieci lub w aplikacji.

Kursy
51
Lekcje
203

Często zadawane pytania

Czy lekcja „Reguła trzech/pięciu” jest bezpłatna?

Tak — pełny tekst „Reguła trzech/pięciu” jest dostępny za darmo tutaj w sieci. Aby ćwiczyć ją interaktywnie (wbudowany edytor kodu i tutor AI dostępny 24/7) i odblokować resztę kursu C++ Academy, przejdź na CoddyKit PRO. Kurs C++ Academy zawiera 4 lekcji w sumie.

Co nauczysz się w „Reguła trzech/pięciu”?

Poprawnie zarządzaj kopiowaniem i przenoszeniem Ćwiczysz C++ Academy z praktycznym kodem, który uruchamiasz bezpośrednio w przeglądarce, a tutor AI dostępny 24/7 odpowiada na Twoje pytania podczas pracy nad lekcją.

Czy potrzebuję doświadczenia, aby zacząć C++ Academy?

Nie wymagamy żadnego doświadczenia. C++ Academy w CoddyKit jest strukturyzowany dla początkujących i zaawansowanych użytkowników, więc możesz zacząć tutaj lub od początku i uczyć się w swoim tempie. To lekcja 3 z 4.

Ile czasu zajmuje lekcja „Reguła trzech/pięciu”?

Większość lekcji CoddyKit trwa około 5–10 minut. Każda lekcja to mały, interaktywny krok, dzięki czemu robisz systematyczne postępy i zawsze wracasz dokładnie do tego samego miejsca — na webie i w aplikacji.

Czy mogę pisać i uruchamiać kod w tej lekcji C++ Academy?

Tak. Każda lekcja C++ Academy zawiera wbudowany edytor kodu, więc piszesz i uruchamiasz prawdziwy kod bezpośrednio w przeglądarce i od razu otrzymujesz sprzężenie zwrotne od AI — bez konfiguracji na komputerze.

Wszystkie lekcje w tym kursie

  1. Zasada RAII
  2. Destruktory i sprzątanie
  3. Reguła trzech/pięciu
  4. Strażnicy zakresu
← Powrót do C++ Academy