Progress6 of 20 topics

30% complete

Operators in C++

Operators are special symbols that perform operations on one or more operands and produce a result. C++ provides a rich set of operators for various types of operations, including arithmetic, logical, bitwise, and more. Understanding operators is essential for effective C++ programming.

Key Takeaways

  • C++ provides various types of operators for different operations
  • Operators have precedence and associativity rules that determine evaluation order
  • C++ allows operator overloading, enabling custom behavior for user-defined types
  • Understanding proper operator usage helps prevent common programming errors
  • Operators can be unary (one operand), binary (two operands), or ternary (three operands)

Types of Operators in C++

C++ provides a wide range of operators categorized into different types:

  1. Arithmetic Operators: Perform mathematical calculations
  2. Relational Operators: Compare values
  3. Logical Operators: Perform logical operations
  4. Bitwise Operators: Manipulate individual bits
  5. Assignment Operators: Assign values to variables
  6. Increment/Decrement Operators: Increase or decrease values
  7. Member and Pointer Operators: Access class members and pointers
  8. Other Operators: Special purpose operators like conditional, sizeof, etc.

Arithmetic Operators

Arithmetic operators perform mathematical operations on numeric operands.

OperatorNameExampleResult
+Additiona + bSum of a and b
-Subtractiona - bDifference of a and b
*Multiplicationa * bProduct of a and b
/Divisiona / bQuotient of a divided by b
%Modulusa % bRemainder of a divided by b
+aUnary Plus+aValue of a (rarely used)
-aUnary Minus-aNegated value of a
cpp
1#include <iostream>
2
3int main() {
4 int a = 10, b = 3;
5
6 std::cout << "a = " << a << ", b = " << b << std::endl;
7 std::cout << "a + b = " << a + b << std::endl; // 13
8 std::cout << "a - b = " << a - b << std::endl; // 7
9 std::cout << "a * b = " << a * b << std::endl; // 30
10 std::cout << "a / b = " << a / b << std::endl; // 3 (integer division)
11 std::cout << "a % b = " << a % b << std::endl; // 1 (remainder)
12
13 // Floating-point division
14 double c = 10.0, d = 3.0;
15 std::cout << "c / d = " << c / d << std::endl; // 3.33333...
16
17 // Unary operators
18 int e = 5;
19 std::cout << "+e = " << +e << std::endl; // 5
20 std::cout << "-e = " << -e << std::endl; // -5
21
22 return 0;
23}

Division Behavior

When dividing two integers in C++, the result will be an integer (truncated division). If you want floating-point division, at least one operand must be a floating-point number. The modulus operator % works only with integer operands.

Relational Operators

Relational operators compare two values and return a boolean result (true or false). These operators are often used in conditional statements.

OperatorNameExampleResult
==Equal toa == bTrue if a equals b
!=Not equal toa != bTrue if a is not equal to b
>Greater thana > bTrue if a is greater than b
<Less thana < bTrue if a is less than b
>=Greater than or equal toa >= bTrue if a is greater than or equal to b
<=Less than or equal toa <= bTrue if a is less than or equal to b
cpp
1#include <iostream>
2
3int main() {
4 int a = 10, b = 20, c = 10;
5
6 std::cout << std::boolalpha; // Print true/false instead of 1/0
7
8 std::cout << "a = " << a << ", b = " << b << ", c = " << c << std::endl;
9 std::cout << "a == b: " << (a == b) << std::endl; // false
10 std::cout << "a == c: " << (a == c) << std::endl; // true
11 std::cout << "a != b: " << (a != b) << std::endl; // true
12 std::cout << "a > b: " << (a > b) << std::endl; // false
13 std::cout << "a < b: " << (a < b) << std::endl; // true
14 std::cout << "a >= c: " << (a >= c) << std::endl; // true
15 std::cout << "a <= c: " << (a <= c) << std::endl; // true
16
17 return 0;
18}

Common Mistake:

Don't confuse the equality operator (==) with the assignment operator (=). The equality operator compares two values, while the assignment operator assigns a value to a variable. Using = in a condition will assign a value and then evaluate it as a boolean.

Logical Operators

Logical operators are used to combine multiple conditions or to negate a condition. They operate on boolean values and return boolean results.

OperatorNameExampleResult
&&Logical ANDa && bTrue if both a and b are true
||Logical ORa || bTrue if either a or b is true
!Logical NOT!aTrue if a is false, false if a is true
cpp
1#include <iostream>
2
3int main() {
4 bool a = true, b = false;
5
6 std::cout << std::boolalpha; // Print true/false instead of 1/0
7
8 std::cout << "a = " << a << ", b = " << b << std::endl;
9 std::cout << "a && b: " << (a && b) << std::endl; // false
10 std::cout << "a || b: " << (a || b) << std::endl; // true
11 std::cout << "!a: " << (!a) << std::endl; // false
12 std::cout << "!b: " << (!b) << std::endl; // true
13
14 // Combining logical operators
15 std::cout << "!(a && b): " << (!(a && b)) << std::endl; // true
16 std::cout << "!a || !b: " << (!a || !b) << std::endl; // true (De Morgan's Law)
17
18 // Short-circuit evaluation
19 int x = 5, y = 0;
20
21 // The second condition is not evaluated if the first is false (AND)
22 if (y != 0 && x / y > 2) {
23 std::cout << "x / y > 2" << std::endl;
24 } else {
25 std::cout << "Condition not met or division by zero avoided" << std::endl;
26 }
27
28 // The second condition is not evaluated if the first is true (OR)
29 if (y == 0 || x / y > 2) {
30 std::cout << "Either y is zero or x / y > 2" << std::endl;
31 }
32
33 return 0;
34}

Short-Circuit Evaluation

C++ uses short-circuit evaluation for logical operators:

  • For &&, if the first operand is false, the second operand is not evaluated because the result will always be false.
  • For ||, if the first operand is true, the second operand is not evaluated because the result will always be true.
This behavior is useful for avoiding potential errors, like preventing division by zero as shown in the example.

Bitwise Operators

Bitwise operators manipulate individual bits of integer values. They are useful for low-level programming, optimization, and working with hardware interfaces.

OperatorNameExampleResult
&Bitwise ANDa & b1 if both bits are 1, otherwise 0
|Bitwise ORa | b1 if either bit is 1, otherwise 0
^Bitwise XORa ^ b1 if bits are different, otherwise 0
~Bitwise NOT~aInverts all bits (0 becomes 1, 1 becomes 0)
<<Left shifta << bShifts bits of a left by b positions
>>Right shifta >> bShifts bits of a right by b positions
cpp
1#include <iostream>
2#include <bitset>
3
4int main() {
5 unsigned char a = 5; // 00000101 in binary
6 unsigned char b = 3; // 00000011 in binary
7
8 std::cout << "a = " << static_cast<int>(a) << " (" << std::bitset<8>(a) << ")" << std::endl;
9 std::cout << "b = " << static_cast<int>(b) << " (" << std::bitset<8>(b) << ")" << std::endl;
10
11 // Bitwise AND
12 std::cout << "a & b = " << static_cast<int>(a & b)
13 << " (" << std::bitset<8>(a & b) << ")" << std::endl;
14
15 // Bitwise OR
16 std::cout << "a | b = " << static_cast<int>(a | b)
17 << " (" << std::bitset<8>(a | b) << ")" << std::endl;
18
19 // Bitwise XOR
20 std::cout << "a ^ b = " << static_cast<int>(a ^ b)
21 << " (" << std::bitset<8>(a ^ b) << ")" << std::endl;
22
23 // Bitwise NOT
24 std::cout << "~a = " << static_cast<int>(~a)
25 << " (" << std::bitset<8>(~a) << ")" << std::endl;
26
27 // Left shift
28 std::cout << "a << 1 = " << static_cast<int>(a << 1)
29 << " (" << std::bitset<8>(a << 1) << ")" << std::endl;
30
31 // Right shift
32 std::cout << "a >> 1 = " << static_cast<int>(a >> 1)
33 << " (" << std::bitset<8>(a >> 1) << ")" << std::endl;
34
35 return 0;
36}

Common Bitwise Operations

  • Setting a bit: x |= (1 << n) - Sets the nth bit to 1
  • Clearing a bit: x &= ~(1 << n) - Sets the nth bit to 0
  • Toggling a bit: x ^= (1 << n) - Flips the nth bit
  • Checking a bit: (x & (1 << n)) != 0 - Checks if the nth bit is set
  • Multiplication by 2n: x << n - Equivalent to x * 2^n
  • Division by 2n: x >> n - Equivalent to x / 2^n

Assignment Operators

Assignment operators are used to assign values to variables. C++ provides shorthand operators that combine assignment with other operations.

OperatorNameExampleEquivalent to
=Assignmenta = ba = b
+=Addition assignmenta += ba = a + b
-=Subtraction assignmenta -= ba = a - b
*=Multiplication assignmenta *= ba = a * b
/=Division assignmenta /= ba = a / b
%=Modulus assignmenta %= ba = a % b
&=Bitwise AND assignmenta &= ba = a & b
|=Bitwise OR assignmenta |= ba = a | b
^=Bitwise XOR assignmenta ^= ba = a ^ b
<<=Left shift assignmenta <<= ba = a << b
>>=Right shift assignmenta >>= ba = a >> b
cpp
1#include <iostream>
2
3int main() {
4 int a = 10;
5
6 std::cout << "Initial value: a = " << a << std::endl;
7
8 // Addition assignment
9 a += 5; // Equivalent to a = a + 5
10 std::cout << "After a += 5: a = " << a << std::endl;
11
12 // Subtraction assignment
13 a -= 3; // Equivalent to a = a - 3
14 std::cout << "After a -= 3: a = " << a << std::endl;
15
16 // Multiplication assignment
17 a *= 2; // Equivalent to a = a * 2
18 std::cout << "After a *= 2: a = " << a << std::endl;
19
20 // Division assignment
21 a /= 4; // Equivalent to a = a / 4
22 std::cout << "After a /= 4: a = " << a << std::endl;
23
24 // Modulus assignment
25 a %= 2; // Equivalent to a = a % 2
26 std::cout << "After a %= 2: a = " << a << std::endl;
27
28 // Bitwise assignments
29 a = 10; // Reset to 10 (1010 in binary)
30 std::cout << "Reset to: a = " << a << std::endl;
31
32 a &= 12; // 10 & 12 = 1010 & 1100 = 1000 = 8
33 std::cout << "After a &= 12: a = " << a << std::endl;
34
35 a |= 5; // 8 | 5 = 1000 | 0101 = 1101 = 13
36 std::cout << "After a |= 5: a = " << a << std::endl;
37
38 a ^= 9; // 13 ^ 9 = 1101 ^ 1001 = 0100 = 4
39 std::cout << "After a ^= 9: a = " << a << std::endl;
40
41 a <<= 1; // 4 << 1 = 0100 << 1 = 1000 = 8
42 std::cout << "After a <<= 1: a = " << a << std::endl;
43
44 a >>= 2; // 8 >> 2 = 1000 >> 2 = 0010 = 2
45 std::cout << "After a >>= 2: a = " << a << std::endl;
46
47 return 0;
48}

Increment and Decrement Operators

Increment and decrement operators are unary operators that increase or decrease the value of a variable by one. They can be used in prefix or postfix form.

OperatorNameDescription
++aPre-incrementIncrements a by 1, then returns a
a++Post-incrementReturns a, then increments a by 1
--aPre-decrementDecrements a by 1, then returns a
a--Post-decrementReturns a, then decrements a by 1
cpp
1#include <iostream>
2
3int main() {
4 int a = 5, b = 5, c = 5, d = 5;
5 int result;
6
7 // Pre-increment
8 result = ++a; // a is incremented to 6, then assigned to result
9 std::cout << "After ++a: a = " << a << ", result = " << result << std::endl;
10
11 // Post-increment
12 result = b++; // b (5) is assigned to result, then b is incremented to 6
13 std::cout << "After b++: b = " << b << ", result = " << result << std::endl;
14
15 // Pre-decrement
16 result = --c; // c is decremented to 4, then assigned to result
17 std::cout << "After --c: c = " << c << ", result = " << result << std::endl;
18
19 // Post-decrement
20 result = d--; // d (5) is assigned to result, then d is decremented to 4
21 std::cout << "After d--: d = " << d << ", result = " << result << std::endl;
22
23 // Common usage in loops
24 std::cout << "\nCounting up:" << std::endl;
25 for (int i = 1; i <= 3; ++i) { // Pre-increment is generally preferred in loops
26 std::cout << i << " ";
27 }
28
29 std::cout << "\nCounting down:" << std::endl;
30 for (int i = 3; i > 0; --i) { // Pre-decrement is generally preferred in loops
31 std::cout << i << " ";
32 }
33
34 return 0;
35}

Pre vs Post Increment/Decrement:

Though the difference between prefix and postfix forms might seem subtle, it can significantly impact your code:

  • For basic data types, performance is usually the same
  • For custom objects, the postfix form creates a temporary copy, making it potentially less efficient
  • When the result is not used, there's no difference in behavior
  • Prefer prefix form (++i) in loops for potential optimization

Other Operators

C++ provides several other important operators for various purposes.

OperatorNameDescription
? :Conditional (Ternary)Evaluates a condition and returns one of two values
sizeofSize-ofReturns the size of a variable or type in bytes
,CommaEvaluates multiple expressions and returns the last one
.Member accessAccesses members of an object
->Pointer-to-memberAccesses members of an object through a pointer
::Scope resolutionAccesses namespace, class, or enumeration members
.*, ->*Pointer to memberAccess member using pointer to member
typeidType identificationReturns the type identification
new, deleteDynamic memoryAllocates and deallocates memory
cpp
1#include <iostream>
2#include <string>
3#include <typeinfo>
4
5class Person {
6public:
7 std::string name;
8 int age;
9
10 Person(const std::string& n, int a) : name(n), age(a) {}
11
12 void display() const {
13 std::cout << "Name: " << name << ", Age: " << age << std::endl;
14 }
15};
16
17int main() {
18 // Conditional (ternary) operator
19 int x = 10, y = 20;
20 int max = (x > y) ? x : y; // If x > y, max = x, otherwise max = y
21 std::cout << "Max value: " << max << std::endl;
22
23 // sizeof operator
24 std::cout << "Size of int: " << sizeof(int) << " bytes" << std::endl;
25 std::cout << "Size of x: " << sizeof x << " bytes" << std::endl;
26
27 // Comma operator
28 int a, b;
29 a = (b = 3, b + 2); // b is assigned 3, then a is assigned b + 2 (5)
30 std::cout << "a = " << a << ", b = " << b << std::endl;
31
32 // Member access operators
33 Person person("Alice", 30);
34 Person* personPtr = &person;
35
36 // . operator for objects
37 person.display();
38 std::cout << "Using . operator: " << person.name << std::endl;
39
40 // -> operator for pointers to objects
41 personPtr->display();
42 std::cout << "Using -> operator: " << personPtr->name << std::endl;
43
44 // :: scope resolution operator
45 std::cout << "Using :: operator: " << std::endl;
46 std::cout << std::boolalpha; // std:: is using the scope resolution operator
47
48 // typeid operator
49 std::cout << "Type of x: " << typeid(x).name() << std::endl;
50 std::cout << "Type of person: " << typeid(person).name() << std::endl;
51
52 // new and delete operators
53 int* dynamicInt = new int(42);
54 std::cout << "Value of dynamicInt: " << *dynamicInt << std::endl;
55 delete dynamicInt; // Free the allocated memory
56
57 // Array allocation
58 int* array = new int[5]{1, 2, 3, 4, 5};
59 std::cout << "Array values: ";
60 for (int i = 0; i < 5; i++) {
61 std::cout << array[i] << " ";
62 }
63 std::cout << std::endl;
64 delete[] array; // Free the array memory
65
66 return 0;
67}

Operator Overloading

One of the powerful features of C++ is the ability to overload operators for user-defined types. This allows custom classes to use standard operators like +, -, *, etc., with custom behavior.

cpp
1#include <iostream>
2
3class Complex {
4private:
5 double real;
6 double imag;
7
8public:
9 // Constructor
10 Complex(double r = 0.0, double i = 0.0) : real(r), imag(i) {}
11
12 // Overload + operator
13 Complex operator+(const Complex& other) const {
14 return Complex(real + other.real, imag + other.imag);
15 }
16
17 // Overload - operator
18 Complex operator-(const Complex& other) const {
19 return Complex(real - other.real, imag - other.imag);
20 }
21
22 // Overload * operator
23 Complex operator*(const Complex& other) const {
24 return Complex(
25 real * other.real - imag * other.imag,
26 real * other.imag + imag * other.real
27 );
28 }
29
30 // Overload == operator
31 bool operator==(const Complex& other) const {
32 return (real == other.real) && (imag == other.imag);
33 }
34
35 // Overload != operator
36 bool operator!=(const Complex& other) const {
37 return !(*this == other);
38 }
39
40 // Overload unary - operator
41 Complex operator-() const {
42 return Complex(-real, -imag);
43 }
44
45 // Overload prefix increment operator
46 Complex& operator++() {
47 ++real;
48 return *this;
49 }
50
51 // Overload postfix increment operator
52 Complex operator++(int) {
53 Complex temp = *this;
54 ++real;
55 return temp;
56 }
57
58 // Display the complex number
59 void display() const {
60 std::cout << real;
61 if (imag >= 0) {
62 std::cout << " + " << imag << "i";
63 } else {
64 std::cout << " - " << -imag << "i";
65 }
66 }
67
68 // Friend function to overload << operator
69 friend std::ostream& operator<<(std::ostream& os, const Complex& c);
70};
71
72// Overload the << operator for Complex using a friend function
73std::ostream& operator<<(std::ostream& os, const Complex& c) {
74 os << c.real;
75 if (c.imag >= 0) {
76 os << " + " << c.imag << "i";
77 } else {
78 os << " - " << -c.imag << "i";
79 }
80 return os;
81}
82
83int main() {
84 Complex c1(3, 4);
85 Complex c2(1, -2);
86
87 std::cout << "c1 = " << c1 << std::endl;
88 std::cout << "c2 = " << c2 << std::endl;
89
90 // Using overloaded operators
91 Complex sum = c1 + c2;
92 Complex diff = c1 - c2;
93 Complex prod = c1 * c2;
94
95 std::cout << "c1 + c2 = " << sum << std::endl;
96 std::cout << "c1 - c2 = " << diff << std::endl;
97 std::cout << "c1 * c2 = " << prod << std::endl;
98
99 // Using comparison operators
100 std::cout << "c1 == c2: " << (c1 == c2) << std::endl;
101 std::cout << "c1 != c2: " << (c1 != c2) << std::endl;
102
103 // Using unary operator
104 Complex neg = -c1;
105 std::cout << "-c1 = " << neg << std::endl;
106
107 // Using increment operators
108 Complex c3 = c1;
109 std::cout << "Original c3 = " << c3 << std::endl;
110 std::cout << "++c3 = " << ++c3 << std::endl;
111 std::cout << "c3 after prefix increment = " << c3 << std::endl;
112
113 std::cout << "c3++ = " << c3++ << std::endl;
114 std::cout << "c3 after postfix increment = " << c3 << std::endl;
115
116 return 0;
117}

Operator Overloading Guidelines

  • Maintain the original semantics of operators when overloading (e.g., + should be used for addition-like operations)
  • Use member functions for operators that modify the left operand (e.g., +=, -=)
  • Use non-member functions for operators where the left operand might be converted (e.g., allowing int + MyClass)
  • Consider overloading related operators together (e.g., if you overload ==, also overload !=)
  • Make sure to handle self-assignment in assignment operators

Operator Precedence and Associativity

When an expression contains multiple operators, operator precedence and associativity determine the order of evaluation. Operators with higher precedence are evaluated first. Associativity determines the order of evaluation for operators with the same precedence.

Operator Precedence (Highest to Lowest)

  1. Scope resolution ::
  2. Member access . ->, Post-increment/decrement ++ --, Names typeid, Casting
  3. Pre-increment/decrement ++ --, Unary + - ! ~, Size sizeof, Memory new delete
  4. Pointer-to-member .* ->*
  5. Multiplicative * / %
  6. Additive + -
  7. Shift << >>
  8. Relational < <= > >=
  9. Equality == !=
  10. Bitwise AND &
  11. Bitwise XOR ^
  12. Bitwise OR |
  13. Logical AND &&
  14. Logical OR ||
  15. Conditional ?:
  16. Assignment = += -= *= /= %= &= ^= |= <<= >>=
  17. Comma ,
cpp
1#include <iostream>
2
3int main() {
4 int a = 5, b = 3, c = 10;
5 int result;
6
7 // Example 1: Precedence of arithmetic operators
8 result = a + b * c; // * has higher precedence than +
9 std::cout << "a + b * c = " << result << std::endl; // 5 + (3 * 10) = 35
10
11 // Using parentheses to override precedence
12 result = (a + b) * c;
13 std::cout << "(a + b) * c = " << result << std::endl; // (5 + 3) * 10 = 80
14
15 // Example 2: Precedence of logical operators
16 bool test = a > b && c > a; // && has lower precedence than >
17 std::cout << "a > b && c > a = " << std::boolalpha << test << std::endl; // (5 > 3) && (10 > 5) = true
18
19 // Example 3: Assignment vs. equality
20 bool equal = (a = b) == c; // = has lower precedence than ==
21 std::cout << "After a = b, a = " << a << std::endl; // a is now 3
22 std::cout << "(a = b) == c = " << equal << std::endl; // (3) == 10 = false
23
24 // Reset a
25 a = 5;
26
27 // Example 4: Associativity
28 // Left-to-right associativity of *
29 result = a * b * c; // (a * b) * c
30 std::cout << "a * b * c = " << result << std::endl; // (5 * 3) * 10 = 150
31
32 // Right-to-left associativity of =
33 a = b = c; // a = (b = c)
34 std::cout << "After a = b = c: a = " << a << ", b = " << b << ", c = " << c << std::endl;
35
36 return 0;
37}

Best Practice:

When in doubt about operator precedence, use parentheses to make your intention explicit. This improves code readability and prevents unexpected behavior due to misunderstood precedence rules.

Summary

C++ provides a comprehensive set of operators for various operations. Understanding these operators and their behavior is essential for effective C++ programming.

  • Arithmetic operators perform mathematical calculations
  • Relational operators compare values
  • Logical operators combine conditions
  • Bitwise operators manipulate individual bits
  • Assignment operators assign values to variables
  • Increment/decrement operators increase or decrease values by one
  • Other operators like conditional, sizeof, and member access operators serve specific purposes
  • Operator overloading allows custom behavior for user-defined types
  • Operator precedence and associativity determine the order of evaluation

Mastering these operators will allow you to write more concise, efficient, and readable C++ code. Remember to use parentheses when in doubt about operator precedence and to consider the semantics of operators when overloading them for custom types.

Related Tutorials

Learn about variables and data types in C++.

Learn more

Understand control flow statements in C++.

Learn more

Learn how to define and use functions in C++.

Learn more