C Programming Assignment Operators C++

Operator overloading[edit]

Operator overloading (less commonly known as ad-hoc polymorphism) is a specific case of polymorphism (part of the OO nature of the language) in which some or all operators like , or are treated as polymorphic functions and as such have different behaviors depending on the types of its arguments. Operator overloading is usually only syntactic sugar. It can easily be emulated using function calls.

Consider this operation:

Using operator overloading permits a more concise way of writing it, like this:

a + b * c

(Assuming the operator has higher precedence than .)

Operator overloading can provide more than an aesthetic benefit, since the language allows operators to be invoked implicitly in some circumstances. Problems, and critics, to the use of operator overloading arise because it allows programmers to give operators completely free functionality, without an imposition of coherency that permits to consistently satisfy user/reader expectations. Usage of the operator is an example of this problem.

// The expressiona<<1;

Will return twice the value of if is an integer variable, but if is an output stream instead this will write "1" to it. Because operator overloading allows the programmer to change the usual semantics of an operator, it is usually considered good practice to use operator overloading with care.

To overload an operator is to provide it with a new meaning for user-defined types. This is done in the same fashion as defining a function. The basic syntax follows (where @ represents a valid operator):

return_typeoperator@(argument_list){// ... definition}

Not all operators may be overloaded, new operators cannot be created, and the precedence, associativity or arity of operators cannot be changed (for example ! cannot be overloaded as a binary operator). Most operators may be overloaded as either a member function or non-member function, some, however, must be defined as member functions. Operators should only be overloaded where their use would be natural and unambiguous, and they should perform as expected. For example, overloading + to add two complex numbers is a good use, whereas overloading * to push an object onto a vector would not be considered good style.

Operator overloading should only be utilized when the meaning of the overloaded operator's operation is unambiguous and practical for the underlying type and where it would offer a significant notational brevity over appropriately named function calls.

A simple Message Header
// sample of Operator Overloading#include<string>classPlMessageHeader{std::stringm_ThreadSender;std::stringm_ThreadReceiver;//return true if the messages are equal, false otherwiseinlinebooloperator==(constPlMessageHeader&b)const{return((b.m_ThreadSender==m_ThreadSender)&&(b.m_ThreadReceiver==m_ThreadReceiver));}//return true if the message is for nameinlineboolisFor(conststd::string&name)const{return(m_ThreadReceiver==name);}//return true if the message is for nameinlineboolisFor(constchar*name)const{return(m_ThreadReceiver==name);// since name type is std::string, it becomes unsafe if name == NULL}};

The use of the keyword in the example above is technically redundant, as functions defined within a class definition like this are implicitly inline.

Operators as member functions[edit]

Aside from the operators which must be members, operators may be overloaded as member or non-member functions. The choice of whether or not to overload as a member is up to the programmer. Operators are generally overloaded as members when they:

  1. change the left-hand operand, or
  2. require direct access to the non-public parts of an object.

When an operator is defined as a member, the number of explicit parameters is reduced by one, as the calling object is implicitly supplied as an operand. Thus, binary operators take one explicit parameter and unary operators none. In the case of binary operators, the left hand operand is the calling object, and no type coercion will be done upon it. This is in contrast to non-member operators, where the left hand operand may be coerced.

// binary operator as member function//Vector2D Vector2D::operator+(const Vector2D& right)const [...]// binary operator as non-member function//Vector2D operator+(const Vector2D& left, const Vector2D& right)[...]// binary operator as non-member function with 2 arguments //friend Vector2D operator+(const Vector2D& left, const Vector2D& right) [...]// unary operator as member function//Vector2D Vector2D::operator-()const {...}// unary operator as non-member function[...]//Vector2D operator-(const Vector2D& vec) [...]

Overloadable operators[edit]

Arithmetic operators[edit]
  • + (addition)
  • - (subtraction)
  • * (multiplication)
  • / (division)
  • % (modulus)

As binary operators, these involve two arguments which do not have to be the same type. These operators may be defined as member or non-member functions. An example illustrating overloading for the addition of a 2D mathematical vector type follows.


It is good style to only overload these operators to perform their customary arithmetic operation. Because operator has been overloaded as member function, it can access private fields.

Bitwise operators[edit]
  • ^ (XOR)
  • | (OR)
  • & (AND)
  • ~ (complement)
  • << (shift left, insertion to stream)
  • >> (shift right, extraction from stream)

All of the bitwise operators are binary, except complement, which is unary. It should be noted that these operators have a lower precedence than the arithmetic operators, so if ^ were to be overloaded for exponentiation, x ^ y + z may not work as intended. Of special mention are the shift operators, << and >>. These have been overloaded in the standard library for interaction with streams. When overloading these operators to work with streams the rules below should be followed:

  1. overload << and >> as friends (so that it can access the private variables with the stream be passed in by references
  2. (input/output modifies the stream, and copying is not allowed)
  3. the operator should return a reference to the stream it receives (to allow chaining, cout << 3 << 4 << 5)
An example using a 2D vector
friendostream&operator<<(ostream&out,constVector2D&vec)// output{out<<"("<<vec.x()<<", "<<vec.y()<<")";returnout;}friendistream&operator>>(istream&in,Vector2D&vec)// input{doublex,y;// skip opening paranthesisin.ignore(1);// read xin>>x;vec.set_x(x);// skip delimiterin.ignore(2);// read yin>>y;vec.set_y(y);// skip closing paranthesisin.ignore(1);returnin;}
Assignment operator[edit]

The assignment operator, =, must be a member function, and is given default behavior for user-defined classes by the compiler, performing an assignment of every member using its assignment operator. This behavior is generally acceptable for simple classes which only contain variables. However, where a class contains references or pointers to outside resources, the assignment operator should be overloaded (as general rule, whenever a destructor and copy constructor are needed so is the assignment operator), otherwise, for example, two strings would share the same buffer and changing one would change the other.

In this case, an assignment operator should perform two duties:

  1. clean up the old contents of the object
  2. copy the resources of the other object

For classes which contain raw pointers, before doing the assignment, the assignment operator should check for self-assignment, which generally will not work (as when the old contents of the object are erased, they cannot be copied to refill the object). Self assignment is generally a sign of a coding error, and thus for classes without raw pointers, this check is often omitted, as while the action is wasteful of cpu cycles, it has no other effect on the code.

classBuggyRawPointer{// example of super-common mistakeT*m_ptr;public:BuggyRawPointer(T*ptr):m_ptr(ptr){}BuggyRawPointer&operator=(BuggyRawPointerconst&rhs){deletem_ptr;// free resource; // Problem here!m_ptr=0;m_ptr=rhs.m_ptr;return*this;};};BuggyRawPointerx(newT);x=x;// We might expect this to keep x the same. This sets x.m_ptr == 0. Oops!// The above problem can be fixed like so:classWithRawPointer2{T*m_ptr;public:WithRawPointer2(T*ptr):m_ptr(ptr){}WithRawPointer2&operator=(WithRawPointer2const&rhs){if(this!=&rhs){deletem_ptr;// free resource;m_ptr=0;m_ptr=rhs.m_ptr;}return*this;};};WithRawPointer2x2(newT);x2=x2;// x2.m_ptr unchanged.

Another common use of overloading the assignment operator is to declare the overload in the private part of the class and not define it. Thus any code which attempts to do an assignment will fail on two accounts, first by referencing a private member function and second fail to link by not having a valid definition. This is done for classes where copying is to be prevented, and generally done with the addition of a privately declared copy constructor

classDoNotCopyOrAssign{public:DoNotCopyOrAssign(){};private:DoNotCopyOrAssign(DoNotCopyOrAssignconst&);DoNotCopyOrAssign&operator=(DoNotCopyOrAssignconst&);};classMyClass:publicDoNotCopyOrAssign{public:MyClass();};MyClassx,y;x=y;// Fails to compile due to private assignment operator;MyClassz(x);// Fails to compile due to private copy constructor.
Relational operators[edit]
  • == (equality)
  • != (inequality)
  • > (greater-than)
  • < (less-than)
  • >= (greater-than-or-equal-to)
  • <= (less-than-or-equal-to)

All relational operators are binary, and should return either true or false. Generally, all six operators can be based off a comparison function, or each other, although this is never done automatically (e.g. overloading > will not automatically overload < to give the opposite). There are, however, some templates defined in the header <utility>; if this header is included, then it suffices to just overload operator== and operator<, and the other operators will be provided by the STL.

Logical operators[edit]
  • ! (NOT)
  • && (AND)
  • || (OR)

The logical operators AND are used when evaluating two expressions to obtain a single relational result.The operator corresponds to the boolean logical operation AND,which yields true if operands are true,and false otherwise.The following panel shows the result of operator evaluating the expression.

The ! operator is unary, && and || are binary. It should be noted that in normal use, && and || have "short-circuit" behavior, where the right operand may not be evaluated, depending on the left operand. When overloaded, these operators get function call precedence, and this short circuit behavior is lost. It is best to leave these operators alone.


If the result of Function1() is false, then Function2() is not called.


Both Function3() and Function4() will be called no matter what the result of the call is to Function3() This is a waste of CPU processing, and worse, it could have surprising unintended consequences compared to the expected "short-circuit" behavior of the default operators. Consider:

externMyObject*ObjectPointer;boolFunction1(){returnObjectPointer!=null;}boolFunction2(){returnObjectPointer->MyMethod();}MyBoolFunction3(){returnObjectPointer!=null;}MyBoolFunction4(){returnObjectPointer->MyMethod();}booloperator&&(MyBoolconst&,MyBoolconst&);Function1()&&Function2();// Does not execute Function2() when pointer is nullFunction3()&&Function4();// Executes Function4() when pointer is null
Compound assignment operators[edit]
  • += (addition-assignment)
  • -= (subtraction-assignment)
  • *= (multiplication-assignment)
  • /= (division-assignment)
  • %= (modulus-assignment)
  • &= (AND-assignment)
  • |= (OR-assignment)
  • ^= (XOR-assignment)
  • <<= (shift-left-assignment)
  • >>= (shift-right-assignment)

Compound assignment operators should be overloaded as member functions, as they change the left-hand operand. Like all other operators (except basic assignment), compound assignment operators must be explicitly defined, they will not be automatically (e.g. overloading = and + will not automatically overload +=). A compound assignment operator should work as expected: A @= B should be equivalent to A = A @ B. An example of += for a two-dimensional mathematical vector type follows.

Increment and decrement operators[edit]
  • ++ (increment)
  • -- (decrement)

Increment and decrement have two forms, prefix (++i) and postfix (i++). To differentiate, the postfix version takes a dummy integer. Increment and decrement operators are most often member functions, as they generally need access to the private member data in the class. The prefix version in general should return a reference to the changed object. The postfix version should just return a copy of the original value. In a perfect world, A += 1, A = A + 1, A++, ++A should all leave A with the same value.

SomeValue&SomeValue::operator++()// prefix{++data;return*this;}SomeValueSomeValue::operator++(intunused)// postfix{SomeValueresult=*this;++data;returnresult;}

Often one operator is defined in terms of the other for ease in maintenance, especially if the function call is complex.

SomeValueSomeValue::operator++(intunused)// postfix{SomeValueresult=*this;++(*this);// call SomeValue::operator++()returnresult;}
Subscript operator[edit]

The subscript operator, [ ], is a binary operator which must be a member function (hence it takes only one explicit parameter, the index). The subscript operator is not limited to taking an integral index. For instance, the index for the subscript operator for the std::map template is the same as the type of the key, so it may be a string etc. The subscript operator is generally overloaded twice; as a non-constant function (for when elements are altered), and as a constant function (for when elements are only accessed).

Function call operator[edit]

The function call operator, ( ), is generally overloaded to create objects which behave like functions, or for classes that have a primary operation. The function call operator must be a member function, but has no other restrictions - it may be overloaded with any number of parameters of any type, and may return any type. A class may also have several definitions for the function call operator.

Address of, Reference, and Pointer operators[edit]

These three operators, operator&(), operator*() and operator->() can be overloaded. In general these operators are only overloaded for smart pointers, or classes which attempt to mimic the behavior of a raw pointer. The pointer operator, operator->() has the additional requirement that the result of the call to that operator, must return a pointer, or a class with an overloaded operator->(). In general A == *&A should be true.

Note that overloading operator& invokes undefined behavior:

ISO/IEC 14882:2003, Section 5.3.1
The address of an object of incomplete type can be taken, but if the complete type of that object is a class type that declares operator&() as a member function, then the behavior is undefined (and no diagnostic is required).
classT{public:constmemberFunction()const;};// forward declarationclassDullSmartReference;classDullSmartPointer{private:T*m_ptr;public:DullSmartPointer(T*rhs):m_ptr(rhs){};DullSmartReferenceoperator*()const{returnDullSmartReference(*m_ptr);}T*operator->()const{returnm_ptr;}};classDullSmartReference{private:T*m_ptr;public:DullSmartReference(T&rhs):m_ptr(&rhs){}DullSmartPointeroperator&()const{returnDullSmartPointer(m_ptr);}// conversion operatoroperatorT(){return*m_ptr;}};DullSmartPointerdsp(newT);dsp->memberFunction();// calls T::memberFunctionTt;DullSmartReferencedsr(t);dsp=&dsr;t=dsr;// calls the conversion operator

These are extremely simplified examples designed to show how the operators can be overloaded and not the full details of a SmartPointer or SmartReference class. In general you won't want to overload all three of these operators in the same class.

Comma operator[edit]

The comma operator,() , can be overloaded. The language comma operator has left to right precedence, the operator,() has function call precedence, so be aware that overloading the comma operator has many pitfalls.


For non overloaded comma operator, the order of execution will be Function1(), Function2(); With the overloaded comma operator, the compiler can call either Function1(), or Function2() first.

Member Reference operators[edit]

The two member access operators, operator->() and operator->*() can be overloaded. The most common use of overloading these operators is with defining expression template classes, which is not a common programming technique. Clearly by overloading these operators you can create some very unmaintainable code so overload these operators only with great care.

When the -> operator is applied to a pointer value of type (T *), the language dereferences the pointer and applies the . member access operator (so x->m is equivalent to (*x).m). However, when the -> operator is applied to a class instance, it is called as a unary postfix operator; it is expected to return a value to which the -> operator can again be applied. Typically, this will be a value of type (T *), as in the example under Address of, Reference, and Pointer operators above, but can also be a class instance with operator->() defined; the language will call operator->() as many times as necessary until it arrives at a value of type (T *).

Memory management operators[edit]
  • new (allocate memory for object)
  • new[ ] (allocate memory for array)
  • delete (deallocate memory for object)
  • delete[ ] (deallocate memory for array)

The memory management operators can be overloaded to customize allocation and deallocation (e.g. to insert pertinent memory headers). They should behave as expected, new should return a pointer to a newly allocated object on the heap, delete should deallocate memory, ignoring a NULL argument. To overload new, several rules must be followed:

  • new must be a member function
  • the return type must be void*
  • the first explicit parameter must be a size_t value

To overload delete there are also conditions:

  • delete must be a member function (and cannot be virtual)
  • the return type must be void
  • there are only two forms available for the parameter list, and only one of the forms may appear in a class:
Conversion operators[edit]

Conversion operators enable objects of a class to be either implicitly (coercion) or explicitly (casting) converted to another type. Conversion operators must be member functions, and should not change the object which is being converted, so should be flagged as constant functions. The basic syntax of a conversion operator declaration, and declaration for an int-conversion operator follows.

operator''type''()const;// const is not necessary, but is good styleoperatorint()const;

Notice that the function is declared without a return-type, which can easily be inferred from the type of conversion. Including the return type in the function header for a conversion operator is a syntax error.

doubleoperatordouble()const;// error - return type included

Operators which cannot be overloaded[edit]

  • ?: (conditional)
  • . (member selection)
  • .* (member selection with pointer-to-member)
  • :: (scope resolution)
  • (object size information)
  • typeid (object type information)

To understand the reasons why the language doesn't permit these operators to be overloaded, read "Why can't I overload dot, ::, , etc.?" at the Bjarne Stroustrup's C++ Style and Technique FAQ ( http://www.stroustrup.com/bs_faq2.html#overload-dot ).


Once introduced to variables and constants, we can begin to operate with them by using operators. What follows is a complete list of operators. At this point, it is likely not necessary to know all of them, but they are all listed here to also serve as reference.

Assignment operator (=)

The assignment operator assigns a value to a variable.

This statement assigns the integer value to the variable . The assignment operation always takes place from right to left, and never the other way around:

This statement assigns to variable the value contained in variable . The value of at the moment this statement is executed is lost and replaced by the value of .

Consider also that we are only assigning the value of to at the moment of the assignment operation. Therefore, if changes at a later moment, it will not affect the new value taken by .

For example, let's have a look at the following code - I have included the evolution of the content stored in the variables as comments:

This program prints on screen the final values of and (4 and 7, respectively). Notice how was not affected by the final modification of , even though we declared earlier.

Assignment operations are expressions that can be evaluated. That means that the assignment itself has a value, and -for fundamental types- this value is the one assigned in the operation. For example:

In this expression, is assigned the result of adding 2 and the value of another assignment expression (which has itself a value of 5). It is roughly equivalent to:

With the final result of assigning 7 to .

The following expression is also valid in C++:

It assigns 5 to the all three variables: , and ; always from right-to-left.

Arithmetic operators ( +, -, *, /, % )

The five arithmetical operations supported by C++ are:


Operations of addition, subtraction, multiplication and division correspond literally to their respective mathematical operators. The last one, modulo operator, represented by a percentage sign (), gives the remainder of a division of two values. For example:

results in variable containing the value 2, since dividing 11 by 3 results in 3, with a remainder of 2.

Compound assignment (+=, -=, *=, /=, %=, >>=, <<=, &=, ^=, |=)

Compound assignment operators modify the current value of a variable by performing an operation on it. They are equivalent to assigning the result of an operation to the first operand:

expressionequivalent to...

and the same for all other compound assignment operators. For example:

Increment and decrement (++, --)

Some expression can be shortened even more: the increase operator () and the decrease operator () increase or reduce by one the value stored in a variable. They are equivalent to and to , respectively. Thus:

are all equivalent in its functionality; the three of them increase by one the value of .

In the early C compilers, the three previous expressions may have produced different executable code depending on which one was used. Nowadays, this type of code optimization is generally performed automatically by the compiler, thus the three expressions should produce exactly the same executable code.

A peculiarity of this operator is that it can be used both as a prefix and as a suffix. That means that it can be written either before the variable name () or after it (). Although in simple expressions like or , both have exactly the same meaning; in other expressions in which the result of the increment or decrement operation is evaluated, they may have an important difference in their meaning: In the case that the increase operator is used as a prefix () of the value, the expression evaluates to the final value of , once it is already increased. On the other hand, in case that it is used as a suffix (), the value is also increased, but the expression evaluates to the value that x had before being increased. Notice the difference:

Example 1Example 2

In Example 1, the value assigned to is the value of after being increased. While in Example 2, it is the value had before being increased.

Relational and comparison operators ( ==, !=, >, <, >=, <= )

Two expressions can be compared using relational and equality operators. For example, to know if two values are equal or if one is greater than the other.

The result of such an operation is either true or false (i.e., a Boolean value).

The relational operators in C++ are:

Equal to
Not equal to
Less than
Greater than
Less than or equal to
Greater than or equal to

Here there are some examples:

Of course, it's not just numeric constants that can be compared, but just any value, including, of course, variables. Suppose that , and , then:

Be careful! The assignment operator (operator , with one equal sign) is not the same as the equality comparison operator (operator , with two equal signs); the first one () assigns the value on the right-hand to the variable on its left, while the other () compares whether the values on both sides of the operator are equal. Therefore, in the last expression (), we first assigned the value to and then we compared it to (that also stores the value 2), yielding .

Logical operators ( !, &&, || )

The operator is the C++ operator for the Boolean operation NOT. It has only one operand, to its right, and inverts it, producing if its operand is , and if its operand is . Basically, it returns the opposite Boolean value of evaluating its operand. For example:

The logical operators and are used when evaluating two expressions to obtain a single relational result. The operator corresponds to the Boolean logical operation AND, which yields if both its operands are , and otherwise. The following panel shows the result of operator evaluating the expression :

&& OPERATOR (and)

The operator corresponds to the Boolean logical operation OR, which yields if either of its operands is , thus being false only when both operands are false. Here are the possible results of :

|| OPERATOR (or)

For example:

When using the logical operators, C++ only evaluates what is necessary from left to right to come up with the combined relational result, ignoring the rest. Therefore, in the last example (), C++ evaluates first whether is , and if so, it never checks whether is or not. This is known as short-circuit evaluation, and works like this for these operators:

if the left-hand side expression is , the combined result is (the right-hand side expression is never evaluated).
if the left-hand side expression is , the combined result is (the right-hand side expression is never evaluated).

This is mostly important when the right-hand expression has side effects, such as altering values:

Here, the combined conditional expression would increase by one, but only if the condition on the left of is , because otherwise, the condition on the right-hand side () is never evaluated.

Conditional ternary operator ( ? )

The conditional operator evaluates an expression, returning one value if that expression evaluates to , and a different one if the expression evaluates as . Its syntax is:

If is , the entire expression evaluates to , and otherwise to .

For example:

In this example, was 2, and was 7, so the expression being evaluated () was not , thus the first value specified after the question mark was discarded in favor of the second value (the one after the colon) which was (with a value of 7).

Comma operator ( , )

The comma operator () is used to separate two or more expressions that are included where only one expression is expected. When the set of expressions has to be evaluated for a value, only the right-most expression is considered.

For example, the following code:

would first assign the value 3 to , and then assign to variable . So, at the end, variable would contain the value 5 while variable would contain value 3.

Bitwise operators ( &, |, ^, ~, <<, >> )

Bitwise operators modify variables considering the bit patterns that represent the values they store.

operatorasm equivalentdescription
Bitwise AND
Bitwise inclusive OR
Bitwise exclusive OR
Unary complement (bit inversion)
Shift bits left
Shift bits right

Explicit type casting operator

Type casting operators allow to convert a value of a given type to another type. There are several ways to do this in C++. The simplest one, which has been inherited from the C language, is to precede the expression to be converted by the new type enclosed between parentheses (()):

The previous code converts the floating-point number to an integer value (); the remainder is lost. Here, the typecasting operator was . Another way to do the same thing in C++ is to use the functional notation preceding the expression to be converted by the type and enclosing the expression between parentheses:

Both ways of casting types are valid in C++.


This operator accepts one parameter, which can be either a type or a variable, and returns the size in bytes of that type or object:

Here, is assigned the value , because is a type with a size of one byte.

The value returned by is a compile-time constant, so it is always determined before program execution.

Other operators

Later in these tutorials, we will see a few more operators, like the ones referring to pointers or the specifics for object-oriented programming.

Precedence of operators

A single expression may have multiple operators. For example:

In C++, the above expression always assigns 6 to variable , because the operator has a higher precedence than the operator, and is always evaluated before. Parts of the expressions can be enclosed in parenthesis to override this precedence order, or to make explicitly clear the intended effect. Notice the difference:

From greatest to smallest priority, C++ operators are evaluated in the following order:
LevelPrecedence groupOperatorDescriptionGrouping
1Scopescope qualifierLeft-to-right
2Postfix (unary)postfix increment / decrementLeft-to-right
functional forms
member access
3Prefix (unary)prefix increment / decrementRight-to-left
bitwise NOT / logical NOT
unary prefix
reference / dereference
allocation / deallocation
parameter pack
C-style type-casting
4Pointer-to-memberaccess pointerLeft-to-right
5Arithmetic: scalingmultiply, divide, moduloLeft-to-right
6Arithmetic: additionaddition, subtractionLeft-to-right
7Bitwise shiftshift left, shift rightLeft-to-right
8Relationalcomparison operatorsLeft-to-right
9Equalityequality / inequalityLeft-to-right
10Andbitwise ANDLeft-to-right
11Exclusive orbitwise XORLeft-to-right
12Inclusive orbitwise ORLeft-to-right
13Conjunctionlogical ANDLeft-to-right
14Disjunctionlogical ORLeft-to-right
15Assignment-level expressionsassignment / compound assignmentRight-to-left
conditional operator
16Sequencingcomma separatorLeft-to-right

When an expression has two operators with the same precedence level, grouping determines which one is evaluated first: either left-to-right or right-to-left.

Enclosing all sub-statements in parentheses (even those unnecessary because of their precedence) improves code readability.

0 Thoughts to “C Programming Assignment Operators C++

Leave a comment

L'indirizzo email non verrà pubblicato. I campi obbligatori sono contrassegnati *