21.12 — Overloading the assignment operator
The copy assignment operator (operator=) is used to copy values from one object to another already existing object .
As of C++11, C++ also supports “Move assignment”. We discuss move assignment in lesson 22.3 -- Move constructors and move assignment .
Copy assignment vs Copy constructor
The purpose of the copy constructor and the copy assignment operator are almost equivalent -- both copy one object to another. However, the copy constructor initializes new objects, whereas the assignment operator replaces the contents of existing objects.
The difference between the copy constructor and the copy assignment operator causes a lot of confusion for new programmers, but it’s really not all that difficult. Summarizing:
- If a new object has to be created before the copying can occur, the copy constructor is used (note: this includes passing or returning objects by value).
- If a new object does not have to be created before the copying can occur, the assignment operator is used.
Overloading the assignment operator
Overloading the copy assignment operator (operator=) is fairly straightforward, with one specific caveat that we’ll get to. The copy assignment operator must be overloaded as a member function.
This should all be pretty straightforward by now. Our overloaded operator= returns *this, so that we can chain multiple assignments together:
Issues due to self-assignment
Here’s where things start to get a little more interesting. C++ allows self-assignment:
This will call f1.operator=(f1), and under the simplistic implementation above, all of the members will be assigned to themselves. In this particular example, the self-assignment causes each member to be assigned to itself, which has no overall impact, other than wasting time. In most cases, a self-assignment doesn’t need to do anything at all!
However, in cases where an assignment operator needs to dynamically assign memory, self-assignment can actually be dangerous:
First, run the program as it is. You’ll see that the program prints “Alex” as it should.
Now run the following program:
You’ll probably get garbage output. What happened?
Consider what happens in the overloaded operator= when the implicit object AND the passed in parameter (str) are both variable alex. In this case, m_data is the same as str.m_data. The first thing that happens is that the function checks to see if the implicit object already has a string. If so, it needs to delete it, so we don’t end up with a memory leak. In this case, m_data is allocated, so the function deletes m_data. But because str is the same as *this, the string that we wanted to copy has been deleted and m_data (and str.m_data) are dangling.
Later on, we allocate new memory to m_data (and str.m_data). So when we subsequently copy the data from str.m_data into m_data, we’re copying garbage, because str.m_data was never initialized.
Detecting and handling self-assignment
Fortunately, we can detect when self-assignment occurs. Here’s an updated implementation of our overloaded operator= for the MyString class:
By checking if the address of our implicit object is the same as the address of the object being passed in as a parameter, we can have our assignment operator just return immediately without doing any other work.
Because this is just a pointer comparison, it should be fast, and does not require operator== to be overloaded.
When not to handle self-assignment
Typically the self-assignment check is skipped for copy constructors. Because the object being copy constructed is newly created, the only case where the newly created object can be equal to the object being copied is when you try to initialize a newly defined object with itself:
In such cases, your compiler should warn you that c is an uninitialized variable.
Second, the self-assignment check may be omitted in classes that can naturally handle self-assignment. Consider this Fraction class assignment operator that has a self-assignment guard:
If the self-assignment guard did not exist, this function would still operate correctly during a self-assignment (because all of the operations done by the function can handle self-assignment properly).
Because self-assignment is a rare event, some prominent C++ gurus recommend omitting the self-assignment guard even in classes that would benefit from it. We do not recommend this, as we believe it’s a better practice to code defensively and then selectively optimize later.
The copy and swap idiom
A better way to handle self-assignment issues is via what’s called the copy and swap idiom. There’s a great writeup of how this idiom works on Stack Overflow .
The implicit copy assignment operator
Unlike other operators, the compiler will provide an implicit public copy assignment operator for your class if you do not provide a user-defined one. This assignment operator does memberwise assignment (which is essentially the same as the memberwise initialization that default copy constructors do).
Just like other constructors and operators, you can prevent assignments from being made by making your copy assignment operator private or using the delete keyword:
Note that if your class has const members, the compiler will instead define the implicit operator= as deleted. This is because const members can’t be assigned, so the compiler will assume your class should not be assignable.
If you want a class with const members to be assignable (for all members that aren’t const), you will need to explicitly overload operator= and manually assign each non-const member.
- C++ Overview
- C++ Environment Setup
- C++ Basic Syntax
- C++ Comments
- C++ Data Types
- C++ Variable Types
- C++ Variable Scope
- C++ Constants/Literals
- C++ Modifier Types
- C++ Storage Classes
- C++ Operators
- C++ Loop Types
- C++ Decision Making
- C++ Functions
- C++ Numbers
- C++ Strings
- C++ Pointers
- C++ References
- C++ Date & Time
- C++ Basic Input/Output
- C++ Data Structures
- C++ Object Oriented
- C++ Classes & Objects
- C++ Inheritance
- C++ Overloading
- C++ Polymorphism
- C++ Abstraction
- C++ Encapsulation
- C++ Interfaces
- C++ Advanced
- C++ Files and Streams
- C++ Exception Handling
- C++ Dynamic Memory
- C++ Namespaces
- C++ Templates
- C++ Preprocessor
- C++ Signal Handling
- C++ Multithreading
- C++ Web Programming
- C++ Useful Resources
- C++ Questions and Answers
- C++ Quick Guide
- C++ STL Tutorial
- C++ Standard Library
- C++ Discussion
- Selected Reading
- UPSC IAS Exams Notes
- Developer's Best Practices
- Questions and Answers
- Effective Resume Writing
- HR Interview Questions
- Computer Glossary
Assignment Operators Overloading in C++
You can overload the assignment operator (=) just as you can other operators and it can be used to create an object just like the copy constructor.
Following example explains how an assignment operator can be overloaded.
When the above code is compiled and executed, it produces the following result −
Copy assignment operator.
A copy assignment operator is a non-template non-static member function with the name operator = that can be called with an argument of the same class type and copies the content of the argument without mutating the argument.
[ edit ] Syntax
For the formal copy assignment operator syntax, see function declaration . The syntax list below only demonstrates a subset of all valid copy assignment operator syntaxes.
[ edit ] Explanation
The copy assignment operator is called whenever selected by overload resolution , e.g. when an object appears on the left side of an assignment expression.
[ edit ] Implicitly-declared copy assignment operator
If no user-defined copy assignment operators are provided for a class type, the compiler will always declare one as an inline public member of the class. This implicitly-declared copy assignment operator has the form T & T :: operator = ( const T & ) if all of the following is true:
- each direct base B of T has a copy assignment operator whose parameters are B or const B & or const volatile B & ;
- each non-static data member M of T of class type or array of class type has a copy assignment operator whose parameters are M or const M & or const volatile M & .
Otherwise the implicitly-declared copy assignment operator is declared as T & T :: operator = ( T & ) .
Due to these rules, the implicitly-declared copy assignment operator cannot bind to a volatile lvalue argument.
A class can have multiple copy assignment operators, e.g. both T & T :: operator = ( T & ) and T & T :: operator = ( T ) . If some user-defined copy assignment operators are present, the user may still force the generation of the implicitly declared copy assignment operator with the keyword default . (since C++11)
The implicitly-declared (or defaulted on its first declaration) copy assignment operator has an exception specification as described in dynamic exception specification (until C++17) noexcept specification (since C++17)
Because the copy assignment operator is always declared for any class, the base class assignment operator is always hidden. If a using-declaration is used to bring in the assignment operator from the base class, and its argument type could be the same as the argument type of the implicit assignment operator of the derived class, the using-declaration is also hidden by the implicit declaration.
[ edit ] Implicitly-defined copy assignment operator
If the implicitly-declared copy assignment operator is neither deleted nor trivial, it is defined (that is, a function body is generated and compiled) by the compiler if odr-used or needed for constant evaluation (since C++14) . For union types, the implicitly-defined copy assignment copies the object representation (as by std::memmove ). For non-union class types, the operator performs member-wise copy assignment of the object's direct bases and non-static data members, in their initialization order, using built-in assignment for the scalars, memberwise copy-assignment for arrays, and copy assignment operator for class types (called non-virtually).
[ edit ] Deleted copy assignment operator
An implicitly-declared or explicitly-defaulted (since C++11) copy assignment operator for class T is undefined (until C++11) defined as deleted (since C++11) if any of the following conditions is satisfied:
- T has a non-static data member of a const-qualified non-class type (or possibly multi-dimensional array thereof).
- T has a non-static data member of a reference type.
- T has a potentially constructed subobject of class type M (or possibly multi-dimensional array thereof) such that the overload resolution as applied to find M 's copy assignment operator
- does not result in a usable candidate, or
- in the case of the subobject being a variant member , selects a non-trivial function.
[ edit ] Trivial copy assignment operator
The copy assignment operator for class T is trivial if all of the following is true:
- it is not user-provided (meaning, it is implicitly-defined or defaulted);
- T has no virtual member functions;
- T has no virtual base classes;
- the copy assignment operator selected for every direct base of T is trivial;
- the copy assignment operator selected for every non-static class type (or array of class type) member of T is trivial.
A trivial copy assignment operator makes a copy of the object representation as if by std::memmove . All data types compatible with the C language (POD types) are trivially copy-assignable.
[ edit ] Eligible copy assignment operator
Triviality of eligible copy assignment operators determines whether the class is a trivially copyable type .
[ edit ] Notes
If 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 lvalue (named object or a function/operator returning lvalue reference). If only the copy assignment is provided, all argument categories select it (as long as it takes its argument by value or as reference to const, since rvalues can bind to const references), which makes copy assignment the fallback for move assignment, when move is unavailable.
It is unspecified whether virtual base class subobjects that are accessible through more than one path in the inheritance lattice, are assigned more than once by the implicitly-defined copy assignment operator (same applies to move assignment ).
See assignment operator overloading for additional detail on the expected behavior of a user-defined copy-assignment operator.
[ edit ] Example
[ edit ] defect reports.
The following behavior-changing defect reports were applied retroactively to previously published C++ standards.
[ edit ] See also
- converting constructor
- copy constructor
- copy elision
- default constructor
- aggregate initialization
- constant initialization
- copy initialization
- default initialization
- direct initialization
- initializer list
- list initialization
- reference initialization
- value initialization
- zero initialization
- move assignment
- move constructor
- Recent changes
- Offline version
- What links here
- Related changes
- Upload file
- Special pages
- Printable version
- Permanent link
- Page information
- In other languages
- This page was last modified on 2 February 2024, at 15:13.
- This page has been accessed 1,333,785 times.
- About cppreference.com
Overloading assignments (C++ only)
You overload the assignment operator, operator= , with a nonstatic member function that has only one parameter. You cannot declare an overloaded assignment operator that is a nonmember function. The following example shows how you can overload the assignment operator for a particular class:
The assignment x1 = x2 calls the copy assignment operator X& X::operator=(X&) . The assignment x1 = 5 calls the copy assignment operator X& X::operator=(int) . The compiler implicitly declares a copy assignment operator for a class if you do not define one yourself. Consequently, the copy assignment operator ( operator= ) of a derived class hides the copy assignment operator of its base class.
However, you can declare any copy assignment operator as virtual. The following example demonstrates this:
The following is the output of the above example:
The assignment *ap1 = 'z' calls A& A::operator=(char) . Because this operator has not been declared virtual , the compiler chooses the function based on the type of the pointer ap1 . The assignment *ap2 = b2 calls B& B::operator=(const &A) . Because this operator has been declared virtual , the compiler chooses the function based on the type of the object that the pointer ap1 points to. The compiler would not allow the assignment c1 = 'z' because the implicitly declared copy assignment operator declared in class C hides B& B::operator=(char) .
- Copy assignment operators (C++ only)
- Assignment operators
- All Platforms
- First Naukri
- All Companies
- Cognizant GenC
- Cognizant GenC Next
- Cognizant GenC Elevate
- Goldman Sachs
- Infosys SP and DSE
- TCS CodeVita
- TCS Digital
- TCS iON CCQT
- TCS Smart Hiring
- Tech Mahindra
- Zs Associates
- Top 100 Codes
- Learn Python
- Learn Data Structures
- Learn Competitve & Advanced Coding
- Learn Operating System
- Software Engineering
- Online Compiler
- Microsoft Coding Questions
- Amazon Coding Questions
- Learn Logical
- Learn Verbal
- Learn Data Interp.
- Psychometric Test
- All Syllabus
- Cognizant-Off Campus
- L&T Infotech
- Mahindra ComViva
- Reliance Jio
- Wells Fargo
- Interview Preparation
- HR Interview
- Virtual Interview
- Technical Interview
- Group Discussions
- Leadership Questions
- All Interview Exp.
- Accenture ASE
- ZS Associates
- Get OffCampus updates
- On Instagram
- On LinkedIn
- On Telegram
- On Whatsapp
- AMCAT vs CoCubes vs eLitmus vs TCS iON CCQT
- Companies hiring via TCS iON CCQT
- Companies hiring via CoCubes
- Companies hiring via AMCAT
- Companies hiring via eLitmus
- Companies hiring from AMCAT, CoCubes, eLitmus
- Prime Video
- PrepInsta Prime
- Placement Stats
Notifications Mark All Read
No New notification
- Get Prime
Assignment Operator Overloading in C++
February 8, 2023
What is assignment operator overloading in C++?
The assignment operator is a binary operator that is used to assign the value to the variables. It is represented by equal to symbol(=). It copies the right value into the left value i.e the value that is on the right side of equal to into the variable that is on the left side of equal to.
Overloading assignment operator in C++
- Overloading assignment operator in C++ copies all values of one object to another object.
- The object from which values are being copied is known as an instance variable.
- A non-static member function should be used to overload the assignment operator.
The compiler generates the function to overload the assignment operator if the function is not written in the class. The overloading assignment operator can be used to create an object just like the copy constructor. If a new object does not have to be created before the copying occurs, the assignment operator is used, and if the object is created then the copy constructor will come into the picture. Below is a program to explain how the assignment operator overloading works.
History of C++
Structure of a C++ Program
String in C++
Program to check armstrong number or not
C++ program demonstrating assignment operator overloading
Prime course trailer, related banners.
Get PrepInsta Prime & get Access to all 200+ courses offered by PrepInsta in One Subscription
Get over 200+ course One Subscription
Courses like AI/ML, Cloud Computing, Ethical Hacking, C, C++, Java, Python, DSA (All Languages), Competitive Coding (All Languages), TCS, Infosys, Wipro, Amazon, DBMS, SQL and others
Checkout list of all the video courses in PrepInsta Prime Subscription
Login/Signup to comment
Customizes the C++ operators for operands of user-defined types.
Overloaded operators are functions with special function names:
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:
Note: for overloading user-defined conversion functions , user-defined literals , allocation and deallocation see their respective articles.
Overloaded operators (but not the built-in operators) can be called using function notation:
- The operators :: (scope resolution), . (member access), .* (member access through pointer to member), and ?: (ternary conditional) cannot be overloaded.
- New operators such as ** , <> , or &| cannot be created.
- The overloads of operators && and || lose short-circuit evaluation.
- The overload of operator -> must either return a raw pointer, or return an object (by reference or by value) for which operator -> is in turn overloaded.
- It is not possible to change the precedence, grouping, or number of operands of operators.
Other than the restrictions above, the language puts no other constraints on what the overloaded operators do, or on the return type (it does not participate in overload resolution), but in general, overloaded operators are expected to behave as similar as possible to the built-in operators: operator + is expected to add, rather than multiply its arguments, operator = is expected to assign, etc. The related operators are expected to behave similarly ( operator + and operator + = do the same addition-like operation). The return types are limited by the expressions in which the operator is expected to be used: for example, assignment operators return by reference to make it possible to write a = b = c = d , because the built-in operators allow that.
Commonly overloaded operators have the following typical, canonical forms: 
The assignment operator ( operator = ) has special properties: see copy assignment and move assignment for details.
The canonical copy-assignment operator is expected to perform no action on self-assignment , and to return the lhs by reference:
The canonical move assignment is expected to leave the moved-from object in valid state (that is, a state with class invariants intact), and either do nothing or at least leave the object in a valid state on self-assignment, and return the lhs by reference to non-const, and be noexcept:
In those situations where copy assignment cannot benefit from resource reuse (it does not manage a heap-allocated array and does not have a (possibly transitive) member that does, such as a member std::vector or std::string ), there is a popular convenient shorthand: the copy-and-swap assignment operator, which takes its parameter by value (thus working as both copy- and move-assignment depending on the value category of the argument), swaps with the parameter, and lets the destructor clean it up.
This form automatically provides strong exception guarantee , but prohibits resource reuse.
Stream extraction and insertion
The overloads of operator>> and operator<< that take a std:: istream & or std:: ostream & as the left hand argument are known as insertion and extraction operators. Since they take the user-defined type as the right argument ( b in a@b ), they must be implemented as non-members.
These operators are sometimes implemented as friend functions .
Function call operator
When a user-defined class overloads the function call operator, operator ( ) , it becomes a FunctionObject type. Many standard algorithms, from std:: sort to std:: accumulate accept objects of such types to customize behavior. There are no particularly notable canonical forms of operator ( ) , but to illustrate the usage
Increment and decrement
When the postfix increment and decrement appear in an expression, the corresponding user-defined function ( operator ++ or operator -- ) is called with an integer argument 0 . Typically, it is implemented as T operator ++ ( int ) , where the argument is ignored. The postfix increment and decrement operator is usually implemented in terms of the prefix version:
Although canonical form of pre-increment/pre-decrement returns a reference, as with any operator overload, the return type is user-defined; for example the overloads of these operators for std::atomic return by value.
Binary arithmetic operators
Binary operators are typically implemented as non-members to maintain symmetry (for example, when adding a complex number and an integer, if operator+ is a member function of the complex type, then only complex + integer would compile, and not integer + complex ). Since for every binary arithmetic operator there exists a corresponding compound assignment operator, canonical forms of binary operators are implemented in terms of their compound assignments:
Standard algorithms such as std:: sort and containers such as std:: set expect operator < to be defined, by default, for the user-provided types, and expect it to implement strict weak ordering (thus satisfying the Compare requirements). An idiomatic way to implement strict weak ordering for a structure is to use lexicographical comparison provided by std::tie :
Typically, once operator < is provided, the other relational operators are implemented in terms of operator < .
Likewise, the inequality operator is typically implemented in terms of operator == :
When three-way comparison (such as std::memcmp or std::string::compare ) is provided, all six relational operators may be expressed through that:
Array subscript operator
User-defined classes that provide array-like access that allows both reading and writing typically define two overloads for operator [ ] : const and non-const variants:
If the value type is known to be a built-in type, the const variant should return by value.
Where direct access to the elements of the container is not wanted or not possible or distinguishing between lvalue c [ i ] = v ; and rvalue v = c [ i ] ; usage, operator may return a proxy. see for example std::bitset::operator .
To provide multidimensional array access semantics, e.g. to implement a 3D array access a [ i ] [ j ] [ k ] = x ; , operator has to return a reference to a 2D plane, which has to have its own operator which returns a reference to a 1D row, which has to have operator which returns a reference to the element. To avoid this complexity, some libraries opt for overloading operator ( ) instead, so that 3D access expressions have the Fortran-like syntax a ( i, j, k ) = x ;
Bitwise arithmetic operators
User-defined classes and enumerations that implement the requirements of BitmaskType are required to overload the bitwise arithmetic operators operator & , operator | , operator ^ , operator~ , operator & = , operator | = , and operator ^ = , and may optionally overload the shift operators operator << operator >> , operator >>= , and operator <<= . The canonical implementations usually follow the pattern for binary arithmetic operators described above.
Boolean negation operator
The operator operator ! is commonly overloaded by the user-defined classes that are intended to be used in boolean contexts. Such classes also provide a user-defined conversion function explicit operator bool ( ) (see std::basic_ios for the standard library example), and the expected behavior of operator ! is to return the value opposite of operator bool .
Rarely overloaded operators
The following operators are rarely overloaded:
- The address-of operator, operator & . If the unary & is applied to an lvalue of incomplete type and the complete type declares an overloaded operator & , the behavior is undefined (until C++11) it is unspecified whether the operator has the built-in meaning or the operator function is called (since C++11) . Because this operator may be overloaded, generic libraries use std::addressof to obtain addresses of objects of user-defined types. The best known example of a canonical overloaded operator& is the Microsoft class CComPtr . An example of its use in EDSL can be found in boost.spirit .
- The boolean logic operators, operator && and operator || . Unlike the built-in versions, the overloads cannot implement short-circuit evaluation. Also unlike the built-in versions, they do not sequence their left operand before the right one. (until C++17) In the standard library, these operators are only overloaded for std::valarray .
- The comma operator, operator, . Unlike the built-in version, the overloads do not sequence their left operand before the right one. (until C++17) Because this operator may be overloaded, generic libraries use expressions such as a, void ( ) ,b instead of a,b to sequence execution of expressions of user-defined types. The boost library uses operator, in boost.assign , boost.spirit , and other libraries. The database access library SOCI also overloads operator, .
- The member access through pointer to member operator - > * . There are no specific downsides to overloading this operator, but it is rarely used in practice. It was suggested that it could be part of smart pointer interface , and in fact is used in that capacity by actors in boost.phoenix . It is more common in EDSLs such as cpp.react .
The following behavior-changing defect reports were applied retroactively to previously published C++ standards.
- Operator precedence
- Alternative operator syntax
- ↑ Operator Overloading on StackOverflow C++ FAQ
Learn C++ practically and Get Certified .
Popular examples, reference materials, learn c++ interactively, c++ introduction.
- C++ Variables and Literals
- C++ Data Types
- C++ Basic I/O
- C++ Type Conversion
- C++ Operators
- C++ Comments
C++ Flow Control
- C++ if...else
- C++ for Loop
- C++ do...while Loop
- C++ continue
- C++ switch Statement
- C++ goto Statement
- C++ Functions
- C++ Function Types
- C++ Function Overloading
- C++ Default Argument
- C++ Storage Class
- C++ Recursion
- C++ Return Reference
C++ Arrays & String
- Multidimensional Arrays
- C++ Function and Array
- C++ Structures
- Structure and Function
- C++ Pointers to Structure
- C++ Enumeration
C++ Object & Class
- C++ Objects and Class
- C++ Constructors
- C++ Objects & Function
C++ Operator Overloading
- C++ Pointers
- C++ Pointers and Arrays
- C++ Pointers and Functions
- C++ Memory Management
- C++ Inheritance
- Inheritance Access Control
- C++ Function Overriding
- Inheritance Types
- C++ Friend Function
- C++ Virtual Function
- C++ Templates
- Subtract Complex Number Using Operator Overloading
- Increment ++ and Decrement -- Operator Overloading in C++ Programming
- Add Complex Numbers by Passing Structure to a Function
C++ Operator Precedence and Associativity
C++ Ternary Operator
In C++, we can change the way operators work for user-defined types like objects and structures. This is known as operator overloading . For example,
Suppose we have created three objects c1 , c2 and result from a class named Complex that represents complex numbers.
Since operator overloading allows us to change how operators work, we can redefine how the + operator works and use it to add the complex numbers of c1 and c2 by writing the following code:
instead of something like
This makes our code intuitive and easy to understand.
Note: We cannot use operator overloading for fundamental data types like int , float , char and so on.
- Syntax for C++ Operator Overloading
To overload an operator, we use a special operator function. We define the function inside the class or structure whose objects/variables we want the overloaded operator to work with.
- returnType is the return type of the function.
- operator is a keyword.
- symbol is the operator we want to overload. Like: + , < , - , ++ , etc.
- arguments is the arguments passed to the function.
- Operator Overloading in Unary Operators
Unary operators operate on only one operand. The increment operator ++ and decrement operator -- are examples of unary operators.
Example1: ++ Operator (Unary Operator) Overloading
Here, when we use ++count1; , the void operator ++ () is called. This increases the value attribute for the object count1 by 1.
Note: When we overload operators, we can use it to work in any way we like. For example, we could have used ++ to increase value by 100.
However, this makes our code confusing and difficult to understand. It's our job as a programmer to use operator overloading properly and in a consistent and intuitive way.
The above example works only when ++ is used as a prefix. To make ++ work as a postfix we use this syntax.
Notice the int inside the parentheses. It's the syntax used for using unary operators as postfix; it's not a function parameter.
Example 2: ++ Operator (Unary Operator) Overloading
The Example 2 works when ++ is used as both prefix and postfix. However, it doesn't work if we try to do something like this:
This is because the return type of our operator function is void . We can solve this problem by making Count as the return type of the operator function.
Example 3: Return Value from Operator Function (++ Operator)
Here, we have used the following code for prefix operator overloading:
The code for the postfix operator overloading is also similar. Notice that we have created an object temp and returned its value to the operator function.
Also, notice the code
The variable value belongs to the count1 object in main() because count1 is calling the function, while temp.value belongs to the temp object.
- Operator Overloading in Binary Operators
Binary operators work on two operands. For example,
Here, + is a binary operator that works on the operands num and 9 .
When we overload the binary operator for user-defined types by using the code:
The operator function is called using the obj1 object and obj2 is passed as an argument to the function.
Example 4: C++ Binary Operator Overloading
In this program, the operator function is:
Instead of this, we also could have written this function like:
- using & makes our code efficient by referencing the complex2 object instead of making a duplicate object inside the operator function.
- using const is considered a good practice because it prevents the operator function from modifying complex2 .
- Things to Remember in C++ Operator Overloading
- Two operators = and & are already overloaded by default in C++. For example, to copy objects of the same class , we can directly use the = operator. We do not need to create an operator function.
- Operator overloading cannot change the precedence and associativity of operators . However, if we want to change the order of evaluation, parentheses should be used.
- :: (scope resolution)
- . (member selection)
- .* (member selection through pointer to function)
- ?: (ternary operator)
Visit these pages to learn more on:
- How to overload increment operator in right way?
- How to overload binary operator - to subtract complex numbers?
Table of Contents
- Example: ++ Operator (Unary Operator) Overloading
- Example: Return Value from Operator Function (++ Operator)
- Example: C++ Binary Operator Overloading
Sorry about that.
C++ Programming Default Arguments (Parameters)
What Is C++ Overloading Operators And How To Use IT
Get ready to navigate the seas of C++ operator overloading. This essential tool for C++ developers can make your code more intuitive and easier to use. Learn the syntax, best practices, common pitfalls, and frequently asked questions in this comprehensive guide.
💡 KEY INSIGHTS
- Operator overloading in C++ enables you to redefine the behavior of standard operators for custom data types, offering powerful customization and expressive code.
- The article highlights the importance of const-correctness when overloading operators, ensuring the preservation of object states and preventing unintended side effects.
- Understanding the concept of friend functions allows you to access private class members when overloading operators, enhancing encapsulation and maintainability.
- Smart pointers play a pivotal role in safe memory management during operator overloading, reducing the risk of memory leaks and improving code robustness.
In the vast landscape of C++, operator overloading stands as a pillar of efficiency and elegance. This feature, when used judiciously, enables us to extend the logic of built-in types to user-defined types, creating code that's both expressive and intuitive. Yet, without the right guidance, it can also lead us into a labyrinth of complexity. Today, we're tackling this nuanced subject head-on.
Understanding Operator Overloading
Syntax of operator overloading, unary operator overloading, overloading binary operators, overloading assignment operators, overloading the stream operators, operator overloading best practices, common pitfalls and solutions, frequently asked questions.
Operator overloading is a crucial aspect of C++, offering an intuitive way to work with user-defined data types. In essence, this feature allows us to give operators additional meanings when applied to specific classes. Overloading operators effectively leads to syntactic sugar that mimics built-in type behaviors, thus improving code readability and efficiency.
Consider a simple class named 'Vector' that represents a 2D mathematical vector. Without operator overloading, adding two Vector objects might look like this:
However, wouldn't it be more intuitive to use the '+' operator like we do with primitive data types?
That's the primary purpose of operator overloading – to extend the language syntax to user-defined types, making them behave just like built-in types.
However, there are a few operators that cannot be overloaded, including scope (::), sizeOf, member selector (.), and member pointer selector (.*).
Remember that while overloading operators can improve code readability, it should be done wisely.
Let's explore the syntax for operator overloading in C++. Fundamentally, operator overloading is about defining new behaviors for existing operators when they're applied to objects of user-defined classes. We accomplish this by implementing an operator function that gets invoked when the corresponding operator is used.
For instance, if we want to overload the '+' operator for our Vector class, the overloaded function could look something like this:
In the function declaration above, the keyword 'operator' is followed by the symbol of the operator being overloaded ('+'). The function returns a new Vector whose components are the sums of the corresponding components of the two operand vectors.
However, there are cases when overloading as a non-member function is necessary or advantageous. This is particularly true when the left operand of a binary operator isn't an object of our class. For example, to support scalar multiplication with the scalar on the left (like 3 * v ), we'd have to overload the '*' operator as a non-member function:
Note: that the function definition is now outside the class, but still has access to its private members because it's declared as a friend inside the class.
Unary operators are those that act upon a single operand. Common unary operators in C++ include increment (++) and decrement (--), among others. Overloading unary operators in C++ follows a similar syntax and process to overloading binary operators.
For instance, let's assume we want to overload the increment operator (++) for our Vector class to increase both components by 1. The overloaded operator function might look like this:
In the function definition above, the 'operator' keyword is followed by the symbol of the operator being overloaded (++). This function increments the x and y components of the Vector and returns a reference to the vector itself, allowing for chaining operations .
The post-increment/decrement version is overloaded by adding an extra int parameter to the function signature. The int isn't used; it's only a marker distinguishing pre- and post-increment/decrement:
In the example above, the operator++ function takes an unused int parameter, indicating this is a post-increment operation. It first saves the current state, increments the components, and then returns the original state, following the semantics of post-increment.
Binary operators are those that act upon two operands. In C++, common binary operators include arithmetic (+, -, *, /), comparison (==, !=, >, <, >=, <=), and assignment (=), among others.
Overloading Comparison Operators
Overloading assignment operator.
Let's revisit our Vector class to illustrate overloading of the addition operator (+) as a binary operator:
In this example, the operator+ function receives a reference to another Vector and returns a new Vector that is the component-wise sum of the current Vector and the argument Vector. This is an instance of overloading binary operators .
Overloading comparison operators for custom classes can enhance readability and allow the use of these classes in algorithms and data structures that require comparisons.
As an example, we could overload the equality operator (==) for our Vector class as follows:
This implementation of operator== checks if both components of the two vectors are equal and returns the result.
The assignment operator (=) has a special role in C++, and overloading it requires careful consideration. This is particularly true for classes that manage resources, like dynamic memory. However, for simple classes like Vector, overloading can be straightforward:
In this code, we first check for self-assignment (v1 = v1), which could lead to problems in classes managing resources. We then copy the components from the right-hand side vector to the left-hand side vector.
When you write a class, if you don't define an assignment operator, C++ generates one for you. This default assignment operator performs a shallow copy, which might be incorrect for classes managing resources. Here's a simple example for our Vector class:
In this example, operator= copies the values from the input Vector into the current Vector and then returns a reference to the current Vector, allowing chain assignments like v1 = v2 = v3 .
When dealing with resource-managing classes, there's a crucial distinction between copy assignment and move assignment .
Copy assignment involves constructing a new object as a copy of an existing object, while move assignment involves stealing resources from a temporary object that's about to be destroyed (often referred to as an "rvalue").
Here's a simple implementation of copy assignment and move assignment operators for a hypothetical resource-managing class:
In this code, copy assignment makes a new copy of the resource, while move assignment transfers the existing resource, leaving the source object in a safe-to-destruct state.
Stream operators, namely the insertion operator (<<) and the extraction operator (>>), are commonly overloaded in C++ to enable easy output and input of user-defined types. They are typically overloaded for standard streams like std::cout and std::cin .
Overloading the insertion operator (<<) allows us to directly output the contents of an object. For example, for our Vector class, we might implement it as follows:
In this example, the operator<< function outputs the x and y components of the Vector in a specific format. It's declared as a friend function so it can access private members of Vector. It returns a reference to the output stream, allowing chaining of output operations .
Similarly, we can overload the extraction operator (>>) to input the contents of an object. It might look like this for our Vector class:
This function reads in two numbers from the input stream and assigns them to the x and y components of the Vector. Again, the function returns a reference to the input stream, enabling chained input operations .
Operator overloading, when done correctly, can make your C++ code cleaner, more intuitive, and easier to read. However, if used improperly, it can also make your code difficult to understand and debug. Here are some best practices for operator overloading .
Keep It Natural
Be consistent, return type matters, use friend function wisely, self-assignment, overload symmetric operators as non-members.
When overloading an operator, aim to maintain the intuitive meaning of the operator. For example, overloading the addition operator (+) for a Matrix class should result in matrix addition, not subtraction or multiplication.
Ensure consistency between related operators . If you overload the equality operator (==), it's usually a good idea to also overload the inequality operator (!=).
Pay attention to the return type of overloaded operators. For example, assignment and arithmetic operators usually return a reference to the object they are modifying, allowing chain operations.
The friend keyword can grant a function or another class access to a class's private and protected members. While necessary in some cases, such as overloading stream operators, be cautious of overusing it as it can break encapsulation .
Always handle self-assignment correctly in overloaded assignment operators. Failing to do so can lead to hard-to-detect bugs.
When overloading symmetric operators (like arithmetic operators), it's often beneficial to do it as non-member functions (typically as friend functions) to preserve symmetry .
This ensures that expressions like v1 + v2 and v2 + v1 are both valid, assuming v1 and v2 are Vector objects.
If we want users to like our software, we should design it to behave like a likable person.
Source: Techvify Software
Lastly , always remember: overload operators judiciously . Overloading an operator when the operation is counterintuitive or when it doesn't make the code easier to read or maintain can lead to confusing code.
While operator overloading can be a powerful tool in C++, it can also lead to some common pitfalls if not used correctly. Understanding these can help you avoid them and write more efficient and safer code.
Pitfall 1: Overloading The Wrong Operator
Pitfall 2: inconsistent operator overloads, pitfall 3: ignoring self-assignment, pitfall 4: misuse of friend keyword, pitfall 5: forgetting return types.
A common mistake is overloading an operator that does not intuitively match the intended operation. This can lead to code misinterpretation and bugs. The solution? Stick to the natural semantics of operators. For example, use '+' for addition or concatenation, not for subtraction or any other unintuitive operation.
When overloading relational operators (like == and !=), it's important to maintain consistency. If you overload one, overload the others too. Neglecting to do so may lead to unexpected results .
Not handling self-assignment in your overloaded assignment operator can lead to serious bugs . Always include a check to handle this scenario in your implementation.
Overuse of the friend keyword can lead to broken encapsulation and issues in large projects. Use it only when necessary, and prefer member functions whenever possible.
Incorrect or missing return types in overloaded operators can prevent chaining and lead to unexpected behavior . Always specify the return type in your overloaded operator to match the expected behavior of the operator.
When should I overload an operator?
You should consider overloading an operator when it will make your code more intuitive and easier to read and understand. For example, overloading the '+' operator for a Vector class to implement vector addition would be a good use of operator overloading.
Can operators be overloaded for primitive types?
No, operators can only be overloaded for user-defined types (like classes and structs). You cannot overload an operator for primitive types such as int, char, float, etc.
What does 'friend' keyword do in operator overloading?
The 'friend' keyword allows an external function to access the private and protected members of a class. This is useful when overloading certain operators, like the stream operators (<< and >>), that need to be implemented as non-member functions but still need access to private members of the class.
Can I overload an operator without making it a member function?
Yes, an operator can be overloaded as a non-member function using the 'friend' keyword. This is particularly useful for overloading operators where symmetry between the left and right operands is desirable, like arithmetic and comparison operators.
Let’s test your knowledge!
Which Operator Cannot Be Overloaded in C++?
Continue learning with these c++ guides.
- How To Read Numbers Enums From A File C++?
- How To Change Color Of Text C++ Console Cmd?
- C++ Trig Functions: What They Are And How To Use Them
- How Get First Two Digits Of Int C++?
- Resetting A Loop Counter In C++: Best Practices And Examples
Subscribe to our newsletter
Subscribe to be notified of new content on marketsplash..
- C++ Data Types
- C++ Input/Output
- C++ Pointers
- C++ Interview Questions
- C++ Programs
- C++ Cheatsheet
- C++ Projects
- C++ Exception Handling
- C++ Memory Management
- Solve Coding Problems
- C++ Variable Templates
- Unique_ptr in C++
- vTable And vPtr in C++
- Difference Between Compile Time And Run Time Polymorphism In C++
- std::endian in C++ 20
- Nested Try Blocks in C++
- String Tokenization in C
- Variable Shadowing in C++
- Pass By Reference In C
- User-Defined Data Types In C
- Array Decay In C
- Partial Template Specialization in C++
- Decision Making in C++
- Introduction to GUI Programming in C++
- Literals In C++
- Attendance Marking System in C++
- Address Operator & in C
- C++20 std::basic_syncbuf
- std::shared_mutex in C++
Assignment Operators In C++
In C++, the assignment operator forms the backbone of many algorithms and computational processes by performing a simple operation like assigning a value to a variable. It is denoted by equal sign ( = ) and provides one of the most basic operations in any programming language that is used to assign some value to the variables in C++ or in other words, it is used to store some kind of information.
The right-hand side value will be assigned to the variable on the left-hand side. The variable and the value should be of the same data type.
The value can be a literal or another variable of the same data type.
Compound Assignment Operators
In C++, the assignment operator can be combined into a single operator with some other operators to perform a combination of two operations in one single statement. These operators are called Compound Assignment Operators. There are 10 compound assignment operators in C++:
- Addition Assignment Operator ( += )
- Subtraction Assignment Operator ( -= )
- Multiplication Assignment Operator ( *= )
- Division Assignment Operator ( /= )
- Modulus Assignment Operator ( %= )
- Bitwise AND Assignment Operator ( &= )
- Bitwise OR Assignment Operator ( |= )
- Bitwise XOR Assignment Operator ( ^= )
- Left Shift Assignment Operator ( <<= )
- Right Shift Assignment Operator ( >>= )
Lets see each of them in detail.
1. Addition Assignment Operator (+=)
In C++, the addition assignment operator (+=) combines the addition operation with the variable assignment allowing you to increment the value of variable by a specified expression in a concise and efficient way.
This above expression is equivalent to the expression:
2. Subtraction Assignment Operator (-=)
The subtraction assignment operator (-=) in C++ enables you to update the value of the variable by subtracting another value from it. This operator is especially useful when you need to perform subtraction and store the result back in the same variable.
3. Multiplication Assignment Operator (*=)
In C++, the multiplication assignment operator (*=) is used to update the value of the variable by multiplying it with another value.
4. Division Assignment Operator (/=)
The division assignment operator divides the variable on the left by the value on the right and assigns the result to the variable on the left.
5. Modulus Assignment Operator (%=)
The modulus assignment operator calculates the remainder when the variable on the left is divided by the value or variable on the right and assigns the result to the variable on the left.
6. Bitwise AND Assignment Operator (&=)
This operator performs a bitwise AND between the variable on the left and the value on the right and assigns the result to the variable on the left.
7. Bitwise OR Assignment Operator (|=)
The bitwise OR assignment operator performs a bitwise OR between the variable on the left and the value or variable on the right and assigns the result to the variable on the left.
8. Bitwise XOR Assignment Operator (^=)
The bitwise XOR assignment operator performs a bitwise XOR between the variable on the left and the value or variable on the right and assigns the result to the variable on the left.
9. Left Shift Assignment Operator (<<=)
The left shift assignment operator shifts the bits of the variable on the left to left by the number of positions specified on the right and assigns the result to the variable on the left.
10. Right Shift Assignment Operator (>>=)
The right shift assignment operator shifts the bits of the variable on the left to the right by a number of positions specified on the right and assigns the result to the variable on the left.
Also, it is important to note that all of the above operators can be overloaded for custom operations with user-defined data types to perform the operations we want.
Please Login to comment...
- Geeks Premier League 2023
- Geeks Premier League