Reguła trzech (programowanie w C++)
Reguła trzech i reguła pięciu to praktyczne zasady w języku C++ dotyczące budowania kodu odpornego na wyjątki i formalizowania reguł zarządzania zasobami . Reguły określają, w jaki sposób należy używać domyślnych członków klasy , aby systematycznie osiągać te cele.
Zasada trzech
Reguła trzech (znana również jako prawo wielkiej trójki lub wielkiej trójki ) to praktyczna zasada w C++ (przed C++11 ), która głosi, że jeśli klasa definiuje którykolwiek z poniższych elementów, to prawdopodobnie powinna jawnie zdefiniuj wszystkie trzy:
Te trzy funkcje są specjalnymi funkcjami składowymi . Jeśli jedna z tych funkcji zostanie użyta bez uprzedniej deklaracji programisty, zostanie ona niejawnie zaimplementowana przez kompilator z następującą domyślną semantyką:
- Destruktor – wywołaj destruktory wszystkich składowych typu klasy obiektu
- Konstruktor kopiujący - konstruuje wszystkie elementy obiektu z odpowiednich elementów argumentu konstruktora kopiującego, wywołując konstruktory kopiujące elementów typu klasy obiektu i wykonując proste przypisanie wszystkich elementów danych niebędących typem klasy (np. int lub wskaźnik )
- Operator przypisania kopiowania – przypisz wszystkie elementy obiektu z odpowiednich elementów argumentu operatora przypisania, wywołując operatory przypisania kopiowania elementów typu klasy obiektu i wykonując zwykłe przypisanie wszystkich danych nieklasowych (np. int lub wskaźnik ) członkowie.
Reguła trzech głosi, że jeśli jeden z nich musiałby być zdefiniowany przez programistę, oznacza to, że wersja wygenerowana przez kompilator nie pasuje do potrzeb klasy w jednym przypadku i prawdopodobnie nie będzie pasować w innych przypadkach. Termin „reguła trzech” został ukuty przez Marshalla Cline'a w 1991 roku.
Poprawka do tej reguły polega na tym, że jeśli klasa jest zaprojektowana w taki sposób, że pozyskiwanie zasobów jest inicjalizowane (RAII) jest używane dla wszystkich jej (nietrywialnych) członków, destruktor może pozostać niezdefiniowany (znany również jako Prawo Wielkiej Dwójki ). Gotowym przykładem takiego podejścia jest użycie inteligentnych wskaźników zamiast zwykłych.
Ponieważ niejawnie generowane konstruktory i operatory przypisania po prostu kopiują wszystkie składowe danych klasy („ płytka kopia ”), należy zdefiniować jawne konstruktory kopiujące i operatory przypisania kopiowania dla klas, które hermetyzują złożone struktury danych lub mają odwołania zewnętrzne, takie jak wskaźniki, jeśli zachodzi taka potrzeba. skopiuj obiekty wskazane przez członków klasy. Jeśli domyślne zachowanie („płytka kopia”) jest rzeczywiście zamierzone, to wyraźna definicja, choć zbędna, będzie brzmiała „ samodokumentujący się kod " wskazując, że był to raczej zamiar niż przeoczenie. Współczesny C++ zawiera składnię do wyraźnego określania, że pożądana jest funkcja domyślna bez konieczności wpisywania treści funkcji.
Zasada pięciu
Wraz z pojawieniem się C++ 11 reguła trzech może zostać rozszerzona do reguły pięciu (znanej również jako „reguła wielkiej piątki”), ponieważ C++ 11 implementuje semantykę ruchu , umożliwiając chwytanie (lub kradzież ) obiektów docelowych ) dane z obiektów tymczasowych. Poniższy przykład pokazuje również nowe ruchome elementy członkowskie: konstruktor ruchu i operator przypisania ruchu. W związku z tym dla zasady pięciu mamy następujących członków specjalnych :
- burzyciel
- konstruktor kopiujący
- operator przypisania kopii
- konstruktor ruchu
- operator przypisania ruchu
Istnieją sytuacje, w których klasy mogą potrzebować destruktorów, ale nie mogą sensownie zaimplementować konstruktorów kopiowania i przenoszenia oraz operatorów przypisania kopiowania i przenoszenia. Dzieje się tak na przykład, gdy klasa podstawowa nie obsługuje tych ostatnich Wielkiej Czwórki , ale konstruktor klasy pochodnej przydziela pamięć na własny użytek. [ potrzebne źródło ] W C++11 można to uprościć, jawnie określając pięć elementów członkowskich jako domyślnych.