Movatterモバイル変換


[0]ホーム

URL:


cppreference.com
Namespaces
Variants
    Actions

      The rule of three/five/zero

      From cppreference.com
      <cpp‎ |language
       
       
      C++ language
      General topics
      Flow control
      Conditional execution statements
      Iteration statements (loops)
      Jump statements
      Functions
      Function declaration
      Lambda function expression
      inline specifier
      Dynamic exception specifications(until C++17*)
      noexcept specifier(C++11)
      Exceptions
      Namespaces
      Types
      Specifiers
      constexpr(C++11)
      consteval(C++20)
      constinit(C++20)
      Storage duration specifiers
      Initialization
      Expressions
      Alternative representations
      Literals
      Boolean -Integer -Floating-point
      Character -String -nullptr(C++11)
      User-defined(C++11)
      Utilities
      Attributes(C++11)
      Types
      typedef declaration
      Type alias declaration(C++11)
      Casts
      Memory allocation
      Classes
      Class-specific function properties
      Special member functions
      Templates
      Miscellaneous
       

      Contents

      [edit]Rule of three

      If a class requires a user-defineddestructor, a user-definedcopy constructor, or a user-definedcopy assignment operator, it almost certainly requires all three.

      Because C++ copies and copy-assigns objects of user-defined types in various situations (passing/returning by value, manipulating a container, etc), these special member functions will be called, if accessible, and if they are not user-defined, they are implicitly-defined by the compiler.

      The implicitly-defined special member functions should not be used if the classmanages a resource whose handle is an object of non-class type (raw pointer, POSIX file descriptor, etc), whose destructor does nothing and copy constructor/assignment operator performs a "shallow copy" (copies the value of the handle, without duplicating the underlying resource).

      Run this code
      #include <cstddef>#include <cstring>#include <iostream>#include <utility> class rule_of_three{char* cstring;// raw pointer used as a handle to a// dynamically-allocated memory block public:explicit rule_of_three(constchar* s=""): cstring(nullptr){if(s){               cstring= newchar[std::strlen(s)+1];// allocatestd::strcpy(cstring, s);// populate}}     ~rule_of_three()// I. destructor{        delete[] cstring;// deallocate}     rule_of_three(const rule_of_three& other)// II. copy constructor: rule_of_three(other.cstring){}     rule_of_three& operator=(const rule_of_three& other)// III. copy assignment{// implemented through copy-and-swap for brevity// note that this prevents potential storage reuse        rule_of_three temp(other);std::swap(cstring, temp.cstring);return*this;} constchar* c_str()const// accessor{return cstring;}}; int main(){    rule_of_three o1{"abc"};std::cout<< o1.c_str()<<' ';auto o2{o1};// II. uses copy constructorstd::cout<< o2.c_str()<<' ';    rule_of_three o3("def");std::cout<< o3.c_str()<<' ';    o3= o2;// III. uses copy assignmentstd::cout<< o3.c_str()<<'\n';}// I. all destructors are called here

      Output:

      abc abc def abc

      Classes that manage non-copyable resources through copyable handles may have todeclare copy assignment and copy constructorprivate and not provide their definitions(until C++11)define copy assignment and copy constructor as= delete(since C++11). This is another application of the rule of three: deleting one and leaving the other to be implicitly-defined typically incorrect.

      [edit]Rule of five

      Because the presence of a user-defined (include=default or= delete declared) destructor, copy-constructor, or copy-assignment operator prevents implicit definition of themove constructor and themove assignment operator, any class for which move semantics are desirable, has to declare all five special member functions:

      class rule_of_five{char* cstring;// raw pointer used as a handle to a// dynamically-allocated memory blockpublic:explicit rule_of_five(constchar* s=""): cstring(nullptr){if(s){            cstring= newchar[std::strlen(s)+1];// allocatestd::strcpy(cstring, s);// populate}}     ~rule_of_five(){        delete[] cstring;// deallocate}     rule_of_five(const rule_of_five& other)// copy constructor: rule_of_five(other.cstring){}     rule_of_five(rule_of_five&& other)noexcept// move constructor: cstring(std::exchange(other.cstring, nullptr)){}     rule_of_five& operator=(const rule_of_five& other)// copy assignment{// implemented as move-assignment from a temporary copy for brevity// note that this prevents potential storage reusereturn*this= rule_of_five(other);}     rule_of_five& operator=(rule_of_five&& other)noexcept// move assignment{std::swap(cstring, other.cstring);return*this;} // alternatively, replace both assignment operators with copy-and-swap// implementation, which also fails to reuse storage in copy-assignment.//  rule_of_five& operator=(rule_of_five other) noexcept//  {//      std::swap(cstring, other.cstring);//      return *this;//  }};

      Unlike Rule of Three, failing to provide move constructor and move assignment is usually not an error, but a missed optimization opportunity.

      [edit]Rule of zero

      Classes that have custom destructors, copy/move constructors or copy/move assignment operators should deal exclusively with ownership (which follows from theSingle Responsibility Principle). Other classes should not have custom destructors, copy/move constructors or copy/move assignment operators[1].

      This rule also appears in the C++ Core Guidelines asC.20: If you can avoid defining default operations, do.

      class rule_of_zero{std::string cppstring;public:    rule_of_zero(conststd::string& arg): cppstring(arg){}};

      When a base class is intended for polymorphic use, its destructor may have to be declaredpublic andvirtual. This blocks implicit moves (and deprecates implicit copies), and so the special member functions have to be defined as=default[2].

      class base_of_five_defaults{public:    base_of_five_defaults(const base_of_five_defaults&)=default;    base_of_five_defaults(base_of_five_defaults&&)=default;    base_of_five_defaults& operator=(const base_of_five_defaults&)=default;    base_of_five_defaults& operator=(base_of_five_defaults&&)=default;virtual ~base_of_five_defaults()=default;};

      However, this makes the class prone to slicing, which is why polymorphic classes often define copy as= delete (seeC.67: A polymorphic class should suppress public copy/move in C++ Core Guidelines), which leads to the following generic wording for the Rule of Five:

      C.21: If you define or =delete any copy, move, or destructor function, define or =delete them all.

      [edit]External links

      1. "Rule of Zero", R. Martinho Fernandes 08/15/2012
      2. "A Concern about the Rule of Zero", Scott Meyers, 3/13/2014.
      Retrieved from "https://en.cppreference.com/mwiki/index.php?title=cpp/language/rule_of_three&oldid=181459"

      [8]ページ先頭

      ©2009-2025 Movatter.jp