Expressions

Introduction

C++ has a lot of expressions and expression operators

many you have seen before

Arithmetic Operators

+, -, *, / have their usual means in arithmetic expressions

1 + 2 is 3

3 - 4 is -1

5 * 6 is 30

7 / 8 is 0

Arithmetic Operators

% is the mod operator (sometimes called the remainder operator)

6 % 2 is 0

3 % 4 is 3

5 % 5 is 0

15 % 10 is 5

a % b returns the remainder when a is divided by b

Two Kinds of Minus

unary minus: -6, -3.3, -1

binary minus: 8 - 6, 12 - 2, 1 - 1

there’s also unary plus: +6, +3.3, +1

Operator Precedence

unary +, unary - (highest precedence)

*, /, %

binary +, binary - (lowest precedence)

Operator Precedence

-3 + 22 / 11 * 2 - 3 % 2 * 2

same as -3 + 2 * 2 - 3 % 2 * 2

same as -3 + 2 * 2 - 1 * 2

same as -3 + 4 - 1 * 2

same as -3 + 4 - 2

same as -1

Round Brackets

( ) change the order of evaluation

1 + 2 * 3 is 7

(1 + 2) * 3 is 9

use them when you are unsure of operator precedence

Logical and Relational Operators

! not (highest precedence)

<, <=, >, >=

==, !=

&& and

|| or

Logical and Relational Operators

false || !true && 6 < 7

same as false || false && 6 < 7

same as false || false && true

same as false || false

same as false

Logical and Relational Operators

false || !true && 6 < 7

doesn’t compile using our strict warnings-are-errors makefile!

there’s a g++ warning that recommends re-writing the expression like this

false || (!true && 6 < 7)

Short-circuit Evaluation

expr1 && expr2

when this expression is evaluated, first expr1 is evaluated

expr2 is only evaluated if necessary, i.e. if expr1 is false, then expr2 is not evaluated

false && expr2 is assumed to be false no matter the value of expr2

Short-circuit Evaluation

expr1 || expr2

when this expression is evaluated, first expr1 is evaluated

expr2 is only evaluated if necessary, i.e. if expr1 is true, then expr2 is not evaluated

true || expr2 is assumed to be true no matter the value of expr2

Don’t Chain < and Friends

1 < 2 < 3

same as true < 3

same as 1 < 3

same as true

Don’t Chain < and Friends

3 < 2 < 1 (false in mathematics)

same as false < 1

same as 0 < 1

same as true

Don’t Chain < and Friends

don’t write a < b < c (or similar expressions)

instead write a < b && a < c

Avoid Trickiness

C/C++ allows for all kinds of clever ways of mixing operators and types (see the textbook for some examples)

but avoid cleverness

write clear, straightforward code that a programmer can understand with minimal effort

Assignment

x = 5;

= is called the assignment operator

don’t use it in tricky ways (the textbook shows some examples)

== and = are Different

x == y returns either true or false

x = y assigns the value of y to x

Compound Assignment Operators

x += 3 adds 3 to x

y -= 2 subtracts 2 from x

z *= 1.5 multiplies z by 1.5

(see textbook for more such operators)

Increment Operators

++x pre-increment, adds 1 to x and returns the value of x after the increment

x++ post-increment, adds 1 to x and returns the value of x before the increment

Decrement Operators

similar to the increment operators, but instead subtract 1

--x pre-decrement, subtracts 1 from x and returns the value of x before the decrement

x-- post-decrement, subtracts 1 from x and returns the value of x after the decrement

Avoid Operators in Expressions

avoid using ++ and -- in expressions

the order of evaluation may surprise you, e.g.

int x = 4;
cout << x    // prints 5 (!)
     << ++x  // prints 5
     << x    // prints 5
     << endl;

Avoid Incremener/Decrement Operators in Expressions

int x = 4;
cout << x    // prints 5 (!)
     << ++x  // prints 5
     << x    // prints 5
     << endl;

the cout is one big expression and the order of evaluation of the parts of an expression are not guaranteed

Operator Evaluation Order is Not Guaranteed

f() + g()

no guarantee f() is called before g()!

if evaluation order matters, rewrite it, e.g.

double x = f();
double y = g();
cout << x + y;

Member Access Operators

string s = "cat";

cout << s.size();  // . operator accesses a member of an object

string* p = &s;

cout << p->s;     // p -> s is shorthand for (*p).s

cout << *p.size();   // error: . has higher precedence than *

cout << (*p).size(); // ok

The Conditional Operator

string result = (x > 50) ? "pass" : "fail";

if x > 50 is true, evaluate to "pass", else evaluate to "fail"

Conversions

conversions are a mess in C++

lots of rules

some are explicit, some are implicit (happen automatically)

generally, try to avoid mixing different types

Casts

cast are explicit conversions

double x = (double) 4;  // C-style syntax

double y = double(4);   // C++ style syntax

double z = static_cast<double>(4); // C++ template-style syntax

the third style is the most explicit, but it is long and ugly and so we will often use the second style for clarity

const_cast can be used to “cast away const”, i.e. to make a const object non-const (see textbook)

we won’t discuss reinterpret_cast (see textbook)

Casting Advice

generally, avoid casts whenever possible

narrowing casts (e.g. 64 bit type to a 32 bit type) can chop off bits and so lose information

widening casts (e.g. 32 bit type to a 64 bit type) are usually safer

casting away const may be an indication of a design problem