The rule of three/five/zero - cppreference.com (2024)

C++

Compiler support
Freestanding and hosted
Language
Standard library
Standard library headers
Named requirements
Feature test macros (C++20)
Language support library
Concepts library (C++20)
Metaprogramming library (C++11)
Diagnostics library
General utilities library
Strings library
Containers library
Iterators library
Ranges library (C++20)
Algorithms library
Numerics library
Localizations library
Input/output library
Filesystem library (C++17)
Regular expressions library (C++11)
Concurrency support library (C++11)
Technical specifications
Symbols index
External libraries

[edit]

C++ language

General topics
Preprocessor
Comments
Keywords
Escape sequences
Flow control
Conditional execution statements
if
switch
Iteration statements (loops)
for
range-for (C++11)
while
do-while
Jump statements
continue - break
goto - return
Functions
Function declaration
Lambda function expression
inline specifier
Dynamic exception specifications (until C++17*)
noexcept specifier (C++11)
Exceptions
throw-expression
try block

handler
Namespaces

Namespace declaration

Namespace aliases

Types
Fundamental types
Enumeration types
Function types
Class/struct types
Union types

Specifiers
const/volatile
decltype (C++11)
auto (C++11)
constexpr (C++11)
consteval (C++20)
constinit (C++20)
Storage duration specifiers
Initialization
Default-initialization
Value-initialization
Zero-initialization
Copy-initialization
Direct-initialization
Aggregate initialization
List-initialization (C++11)
Constant initialization
Reference initialization

Expressions
Value categories
Order of evaluation
Operators
Operator precedence
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
Implicit conversions
static_cast
const_cast
Explicit conversions
dynamic_cast
reinterpret_cast
Memory allocation

new expression

delete expression

Classes
Class declaration
Constructors
this pointer
Access specifiers
friend specifier

Class-specific function properties
Virtual function
override specifier (C++11)
final specifier (C++11)
explicit (C++11)
static

Special member functions
Default constructor
Copy constructor
Move constructor (C++11)
Copy assignment
Move assignment (C++11)
Destructor
Templates
Class template
Function template
Template specialization
Parameter packs (C++11)
Miscellaneous

Inline assembly

History of C++

[edit]

Contents

  • 1 Rule of three
  • 2 Rule of five
  • 3 Rule of zero
  • 4 External links

[edit] Rule of three

If a class requires a user-defined destructor, a user-defined copy constructor, or a user-defined copy 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 are typically incorrect if the class manages 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" (copy the value of the handle, without duplicating the underlying resource).

Run this code

#include <cstddef>#include <cstring>#include <iostream>class rule_of_three{ char* cstring; // raw pointer used as a handle to a // dynamically-allocated memory block rule_of_three(const char* s, std::size_t n) // to avoid counting twice : cstring(new char[n]) // allocate { std::memcpy(cstring, s, n); // populate }public: explicit rule_of_three(const char* s = "") : rule_of_three(s, std::strlen(s) + 1) {} ~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 { if (this == &other) return *this; std::size_t n{std::strlen(other.cstring) + 1}; char* new_cstring = new char[n]; // allocate std::memcpy(new_cstring, other.cstring, n); // populate delete[] cstring; // deallocate cstring = new_cstring; return *this; } operator const char* () const // accessor { return cstring; }};int main(){ rule_of_three o1{"abc"}; std::cout << o1 << ' '; auto o2{o1}; // II. uses copy constructor std::cout << o2 << ' '; rule_of_three o3("def"); std::cout << o3 << ' '; o3 = o2; // III. uses copy assignment std::cout << o3 << '\n';} // I. all destructors are called here

Output:

abc abc def abc

Classes that manage non-copyable resources through copyable handles may have to declare copy assignment and copy constructor private and not provide their definitions or define them as deleted. This is another application of the rule of three: deleting one and leaving the other to be implicitly-defined will most likely result in errors.

[edit] Rule of five

Because the presence of a user-defined (or = default or = delete declared) destructor, copy-constructor, or copy-assignment operator prevents implicit definition of the move constructor and the move 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(const char* s = "") : cstring(nullptr) { if (s) { std::size_t n = std::strlen(s) + 1; cstring = new char[n]; // allocate std::memcpy(cstring, s, n); // 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 { return *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 // 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 the Single 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 as C.20: If you can avoid defining default operations, do.

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

When a base class is intended for polymorphic use, its destructor may have to be declared public and virtual. This blocks implicit moves (and deprecates implicit copies), and so the special member functions have to be declared as defaulted[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 deleted (see C.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

The rule of three/five/zero - cppreference.com (2024)
Top Articles
Latest Posts
Article information

Author: Otha Schamberger

Last Updated:

Views: 6397

Rating: 4.4 / 5 (55 voted)

Reviews: 86% of readers found this page helpful

Author information

Name: Otha Schamberger

Birthday: 1999-08-15

Address: Suite 490 606 Hammes Ferry, Carterhaven, IL 62290

Phone: +8557035444877

Job: Forward IT Agent

Hobby: Fishing, Flying, Jewelry making, Digital arts, Sand art, Parkour, tabletop games

Introduction: My name is Otha Schamberger, I am a vast, good, healthy, cheerful, energetic, gorgeous, magnificent person who loves writing and wants to share my knowledge and understanding with you.