/ | ||||
(C++11) | ||||
(C++11) |
(C++11) | ||||
(C++20) | ||||
(C++20) |
(C++11) | ||||
expression |
pointer |
specifier | ||||
specifier (C++11) | ||||
specifier (C++11) |
(C++11) | ||||
(C++11) |
(C++11) | ||||
(C++11) |
Rule of three Rule of five Rule of zero External links |
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 should not be used if the class manages a resource whose handle does not destroy the resource themselves (raw pointer, POSIX file descriptor, etc), whose destructor does nothing and copy constructor/assignment operator only copies the value of the handle, without duplicating the underlying resource.
Classes that manage non-copyable resources through copyable handles may have to declare copy assignment and copy constructor private 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.
Because the presence of a user-defined (include = 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:
Unlike Rule of Three, failing to provide move constructor and move assignment is usually not an error, but it will result in a loss of performance.
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 .
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 defined as = default [2] .
However, this makes the class prone to slicing, which is why polymorphic classes often define copy as = delete (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:
. |
Find centralized, trusted content and collaborate around the technologies you use most.
Q&A for work
Connect and share knowledge within a single location that is structured and easy to search.
Get early access and see previews of new features.
What is the correct way to declare instantiation methods when defining an interface class?
Abstract base classes are required to have a virtual destructor for obvious reasons. However, the following compilation warning is then given: "'InterfaceClass' defines a non-default destructor but does not define a copy constructor, a copy assignment operator, a move constructor or a move assignment operator", which is the 'rule of five'.
I understand why the 'rule of five' should be obeyed in general, but is it still applicable for an abstract base class or interface?
My implimentation is then:
Is this correct? Should these methods be = delete instead? Is there some way of declaring the destructor to be virtual pure whilst also somehow remaining default?
Even if I declare the destructor as: virtual ~InterfaceClass() = default; , if I do not explicitly default the other four then I will get the same compiler warning.
Tl;dr: What is the correct way to satisfy the 'rule of five' for an interface class as the user must define a virtual destructor.
Thanks for your time and help!
Is this correct? Should these methods be = delete instead?
Your code seems correct. The need of defining special copy/move member functions as default and protected comes clear when you try to copy a derived class polymorphycally. Consider this additional code:
And consider 4 options for defining special copy/move member functions in your InterfaceClass.
With special copy/move member functions deleted in your InterfaceClass, you would prevent polymorphic copy:
This is good, because polymorphic copy would not be able to copy the data member in the derived class.
On the other hand, you would also prevent normal copy, as the compiler won't be able to implicitly generate a copy assignment operator without the base class copy assignment operator:
With copy/move special member functions as default and public, (or without defining copy/move member functions), normal copy would work:
but polymorphic copy would be enabled and lead to slicing:
If move© special member functions are not defined, behavior with respect to copy is similar to 2: the compiler will implicitly generate deprecated copy special members (leading to polymorphic slicing). However in this case the compiler will not implicitly generate move special members, so copy will be used where a move would be possible.
With special copy/move member functions as default and protected, as in your example, you will prevent polymorphic copy which would otherwise had lead to slicing:
However, the compiler will explicitly generate a default copy assignment operator for InterfaceClass, and ImplementationClass will be able to implicitly generate its copy assignment operator:
So your approach seems the best and safest alternative
For destructor, if you want to make it both pure virtual and default, you can default it in implementation:
It does not make much difference if the destructor is default or empty, though.
Now for the rest of your question.
Typically you should have copy constructor and assignment operator defaulted. This way, they don't prevent making default assignment operators and copy constructor in derived classes. Default implementation is correct, as there's no invariant to copy.
So if you want to implement easily Clone method, deleting copy constructor would harm:
Note also that default implementation of copy/move constructor would not be accidentally used against intention - as instances of abstract base class cannot be created. So you will always be copying derived classes, and they should define, if copying is legal or not.
However, for some classes making copies totally would not make sense, in this case it may be wise to prohibit copying/assigning in the very base class.
Tl;dr: it depend, but most likely you'd better leave them as default.
In general, if any of the big 3 special functions has none-[trivial/default] definition, the other 2 should be defined. If the 2 special move functions have none-[trivial-default] definition, then you need take care of all 5. In the case of an interface with a nop defined dtor, you don't need bother defining the rest - unless for other reasons. Even none-trivial definitions do not nessecitate a redefinition of other functions; only when some sort of resource management(e.g. memory, file, io, sync...) is involved, one need define the big 3(5).
I ran into this problem recently in trying to define an abstract Observer class. Clang-tidy (version 14.0.6) gives a very similar warning to the original, marked [cppcoreguidelines-special-member-functions] . At first I tried to define copy/move constructors and copy/move assignment operators, marked = delete (also tried = default ). Eventually I got a compilation error because the concrete Observer did need a regular constructor to get a Subject pointer to register for notifications.
Upon reflection, however, I think no abstract class really needs a copy/move constructor (and therefore doesn't need to follow the rule of three/five), because by definition, an abstract class cannot be instantiated. So clang-tidy or any other compiler/analyzer that issues a "special member functions" warning on an abstract class is incorrect.
Reminder: Answers generated by artificial intelligence tools are not allowed on Stack Overflow. Learn more
Post as a guest.
Required, but never shown
By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy .
IMAGES
VIDEO
COMMENTS
The assignment operator is not required to be made virtual. The discussion below is about operator=, but it also applies to any operator overloading that takes in the type in question, and any function that takes in the type in question.. The below discussion shows that the virtual keyword does not know about a parameter's inheritance in regards to finding a matching function signature.
1. Virtual assignment is an ungood idea because it introduces run time type-checking, moving bug detection etc. to run-time with more extensive and unreliable testing. - Cheers and hth. - Alf. Jul 15, 2015 at 7:25. I you want to see Calling B message you should try this virtual B& operator=(const A& b_) override { std::cout << "Calling B ...
Correct behavior. CWG 1527. C++11. for assignments to class type objects, the right operand could be an initializer list only when the assignment is defined by a user-defined assignment operator. removed user-defined assignment constraint. CWG 1538. C++11. E1 ={E2} was equivalent to E1 = T(E2) (T is the type of E1), this introduced a C-style cast.
Virtual assignment It is possible to make the assignment operator virtual. However, unlike the destructor case where virtualization is always a good idea, virtualizing the assignment operator really opens up a bag full of worms and gets into some advanced topics outside of the scope of this tutorial.
When an operator appears in an expression, and at least one of its operands has a class type or an enumeration type, then overload resolution is used to determine the user-defined function to be called among all the functions whose signatures match the following: Expression. As member function. As non-member function.
Triviality of eligible copy assignment operators determines whether the class is a trivially copyable type. [] NoteIf both copy and move assignment operators are provided, overload resolution selects the move assignment if the argument is an rvalue (either a prvalue such as a nameless temporary or an xvalue such as the result of std::move), and selects the copy assignment if the argument is an ...
Use an assignment operator operator= that returns a reference to the class type and takes one parameter that's passed by const reference—for example ClassName& operator=(const ClassName& x);. Use the copy constructor. If you don't declare a copy constructor, the compiler generates a member-wise copy constructor for you.
In C++, the multiplication assignment operator (*=) is used to update the value of the variable by multiplying it with another value. Syntax. variable *= value; This above expression is equivalent to the expression: variable = variable * value; Example. C++. #include <iostream>. using namespace std;
It is possible to make the assignment operator virtual. However, unlike the destructor case where virtualization is always a good idea, virtualizing the assignment operator really opens up a bag full of worms and gets into some advanced topics outside of the scope of this tutorial. ... Thank you r/cpp_questions for not being like that! And that ...
It is a bitwise operator. C++ compiler implicitly provides a copy constructor, if no copy constructor is defined in the class. A bitwise copy gets created, if the Assignment operator is not overloaded. Syntax: className (const className &obj) {. // body. } Syntax: className obj1, obj2;
When to use virtual assignment. Assignment, or operator=, is a function that requires careful consideration before you make it virtual. You might think that, like other members, it is better to make it virtual to be safe. Otherwise, you run into problems like this: void Bar (TFoo &arg) { arg = value; } If TFoo::operator= is not virtual, this ...
Moreover, if the destructor of the base class is not virtual, deleting a derived class object through a pointer to the base class is undefined behavior regardless of whether there are resources that would be leaked if the derived destructor is not invoked, unless the selected deallocation function is a destroying operator delete (since C++20).. A useful guideline is that the destructor of any ...
Overloading assignment operator in C++ copies all values of one object to another object. Only a non-static member function should be used to overload the assignment operator. In C++, the compiler automatically provides a default assignment operator for classes. This operator performs a shallow copy of each member of the class from one object ...
The move assignment operator is called whenever it is selected by overload resolution, e.g. when an object appears on the left-hand side of an assignment expression, where the right-hand side is an rvalue of the same or implicitly convertible type.. Move assignment operators typically transfer the resources held by the argument (e.g. pointers to dynamically-allocated objects, file descriptors ...
The move assignment operator was added in C++ 11 to further strengthen the move semantics in C++. It is like a copy assignment operator but instead of copying the data, this moves the ownership of the given data to the destination object without making any additional copies. The source object is left in a valid but unspecified state.
Posted by u/Koalchemy - 2 votes and 10 comments
Got it. A virtual function (also known as virtual methods) is a member function that is declared within a base class and is re-defined (overridden) by a derived class. When you refer to a derived class object using a pointer or a reference to the base class, you can call a virtual function for that object and execute the derived class's ...
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 ...
Abstract base classes are required to have a virtual destructor for obvious reasons. However, the following compilation warning is then given: "'InterfaceClass' defines a non-default destructor but does not define a copy constructor, a copy assignment operator, a move constructor or a move assignment operator", which is the 'rule of five'.