Session 2#
Agenda#
C++ Branching and Loops
C++ Functions
Excercises
C++ Branching and Loops#
clang++ -std=c++17 -o 02_branching_loops 02_branching_loops.cpp
1/**
2 Branching and Loops
3 */
4
5// Include statement
6#include <iostream>
7#include <string>
8#include <vector>
9#include <numeric>
10
11// Namespace declaration statement
12using namespace std;
13
14// Program entry-point, encapsulated by the curly brackets { and }
15int main() {
16
17 // Conditional control with `if`, `else if`, and `else`
18 // The first condition to be satisfied is the code that is executed
19 // Braces, { & }, can be used to to encapsulate conditions that execute multiple statements
20
21 unsigned short int answer = 42;
22 if (answer != 42) {
23 cout << answer << " is the wrong answer.\n";
24 } else {
25 cout << answer << " is the correct answer to life, the universe, and everything.\n";
26 }
27
28 if (answer < 42)
29 cout << answer << " is the wrong answer.\n";
30 else if (answer > 42)
31 cout << answer << " is the wrong answer.\n";
32 else
33 cout << answer << " is the correct answer to life, the universe, and everything.\n";
34
35 // Conditional expressions with the ternary operator
36 // Ternary statements have the form `<condition> ? <result if true> : <result if false>
37
38 answer != 42 ? cout << answer << "is the wrong answer.\n"
39 : cout << answer << " is the correct answer to life, the universe, and everything.\n";
40
41 // Switch statements
42 // Switch statements can be similar to if statements for integral conditions.
43 // They also have the special property where they run one after another until they finish or a `break`.
44
45 string falling_object = "missiles";
46
47 switch (2) {
48 case 2: cout << "The missiles spontaneously transform at a sufficient improbability factor.\n"; falling_object = "bowl of petunias";
49 case 1: cout << "Well, now one of the missiles is a " << falling_object << ".\n"; falling_object = "very surprised whale";
50 case 0: cout << "The other missile is now a " << falling_object << ".\n"; break;
51 default: cout << "Does this run?\n";
52 }
53
54 // While and do-while loops
55 // While loops repeat a section of code until a condition is met.
56 // `while` loops the condition is tested at the beginning of the loop.
57 // Do-while loops the condition is tested at the end of the loop.
58
59 int i = 0;
60 while (i < 10) {
61 cout << "i = " << i << endl;
62 ++i;
63 }
64
65 i = 0;
66 do {
67 cout << "i = " << i << endl;
68 ++i;
69 } while (i < 10);
70
71
72 // `for` loops
73 // `for` loops have three components:
74 // - Initialization
75 // - A continuation condition
76 // - Step operation
77
78 for (int i = 0; i < 10; ++i) {
79 cout << "i = " << i << endl;
80 }
81
82 // Range-base loops
83 // Range-based loops have two components:
84 // - Container element
85 // - Container
86
87 vector<int> v = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
88 for (auto v_i : v)
89 cout << "v_i = " << v_i << endl;
90
91 // Loop control
92 // `break` to stop the loops execution
93 // `continue` to stop the current loop, but continue the loops
94
95 for (int i = -10; i < 100; ++i) {
96 if (i < 0)
97 continue;
98 else if (i < 10)
99 cout << "i = " << i << endl;
100 else
101 break;
102 }
103
104 return 0;
105}
clang++ -std=c++17 -o functions_references_pointers functions_references_pointers.cpp
1/*
2 Functions, References, and Pointers
3
4 Useful References:
5 * Discovering Modern C++ by Peter Gottschling (amongst many other C++ books)
6 * https://en.cppreference.com/
7*/
8
9#include <iostream> // standard IO
10#include <vector> // standard "vector" library for dynamic arrays
11#include <array> // standard "array" library for static arrays
12#include <memory> // For different pointer types, e.g. smart pointers
13
14/*
15
16 The general form of a function in C++ is the following:
17
18 [inline] return_type function_name (argument_list)
19 {
20 body of the function
21 }
22
23 From left to right, inline (and other compiler hints and directives) are
24 optional and come first. Then the return type, which can typically be any
25 datatype, object, or void to return nothing, which is followed by the
26 function name. Finally, all arguments to the function are given in a list.
27
28*/
29
30// Function arguments can be passed by value, reference, or pointer.
31// The resulting behavior in the function and surrounding code change
32// based on that choice.
33//
34// Pass by value
35//
36// When you pass by value, the function makes a copy of the input variable
37// and uses that copy. When the function completes, the copy will be deleted
38// and the original value will be unchanged
39//
40// Pass by reference
41//
42// When passing by reference, the function uses the same variable inside the
43// function as the input. This means that any changes made to that variable
44// will persist after the function is complete
45//
46// Pass by pointer
47//
48// You can also pass values using a pointer, which "points" to a specific
49// location in memory. Since the memory location is the same inside and
50// outside the function, changes to the memory the variable points to
51// will persist after the function is complete
52//
53// Which one should you chose? It depends. For things that are "small"
54// like ints, doubles, etc. It doesn't matter too much.
55// For large data objects (like a large array), copying the entire array
56// may require significantly more resources (time, memory, etc.) So a
57// reference or pointer is a better choice.
58//
59// As an aside. Pointers are the "old" C-style why of doing things. In
60// most cases it is better to use a reference. The code is clearer and
61// it usually also better for memory management reasons. If you need
62// to use pointers, it is usually better to use a smart pointer,
63// shared pointer, unique pointer, etc than the C-style pointer.
64
65// pass an int by value. Increment the value in the
66// function.
67// Expect to see (a+1) inside function, but outside function
68// a remains the initial value
69// return type = void (no return)
70void increment_by_value(int a)
71{
72 ++a; // this is a copy of the input
73 std::cout << "a in function is: " << a << std::endl;
74};
75
76// Pass value by reference instead
77void increment_by_reference(int &a)
78{
79 ++a; // this is the same as the input
80 std::cout << "a in function is: " << a << std::endl;
81};
82
83// pass by points
84void increment_by_pointer(int *a)
85{
86 ++(*a); // this is the same memory location as input
87 std::cout << "a in function is: " << *a << std::endl;
88};
89
90// arguments to functions can either be mutable (meaning they can change)
91// or constant (meaning they cannot be changed by the function)
92// Pass by value is automatically constant because the function operates
93// on a copy and can't modify the input.
94double mutable_compute_norm_squared(std::vector<double> &v)
95{
96 double solution = 0;
97 // loop over each element in the vector
98 for (const auto &element : v)
99 {
100 solution += element * element;
101 }
102
103 // add the solution to the vector to show it can change
104 v.push_back(solution);
105
106 // return the result
107 return solution;
108}
109
110double const_compute_norm_squared(const std::vector<double> &v)
111{
112 double solution = 0;
113 // loop over each element in the vector
114 for (const auto &element : v)
115 {
116 solution += element * element;
117 }
118
119 // this would cause a compiler error because we
120 // cannot change v
121 // v.push_back(solution);
122
123 // return the result
124 return solution;
125}
126
127// We can add default arguments functions.
128// Any inputs with a default must be listed last
129// This will optionally print the answer from inside the function,
130// defaulting to false
131double compute_area(const double &radius, bool print = false)
132{
133 // NOTE: M_PI is defined in most compilers, but not all so use with care
134 const double pi = 3.1415926535897932384;
135 double area = pi * radius * radius;
136
137 if (print)
138 {
139 std::cout << "Inside function, area = " << area << std::endl;
140 }
141
142 return area;
143}
144
145// Overloading.
146//
147// C++ allows you to have multiple functions with the same name
148// as long as the argument list is different.
149double add_numbers(int a, int b)
150{
151 std::cout << "example 1 \n";
152 return a + b;
153}
154double add_numbers(double a, double b)
155{
156 std::cout << "example 2 \n";
157 return a + b;
158}
159double add_numbers(double a, float b, int c)
160{
161 std::cout << "example 3 \n";
162 return a + b + c;
163}
164double add_numbers(int a, double b, float c)
165{
166 std::cout << "example 4 \n";
167 return a + b + c;
168}
169
170// changing the return type, but not the arguments
171// will cause errors
172// int add_numbers(int a, double b, float c)
173//{
174// return a + b + c;
175//}
176//
177// Similarly, chaning whether the functions are pass by value
178// or pass by reference, etc. will cause errors because they
179// compiler won't be able to tell which on you wanted.
180
181int main(int argc, char *argv[])
182{
183
184 // The main function is special. argv contains a list of command line arguments
185 // and argc is the number of arguments. On most systems, there is always 1 argument,
186 // the function name
187 // print out arguments
188 for (int i = 0; i < argc; ++i)
189 {
190 std::cout << "argument " << i << " = " << argv[i] << std::endl;
191 }
192 std::cout << "/n/n";
193
194 int i = 4;
195
196 std::cout << "i = " << i << std::endl;
197
198 std::cout << "\nincrement by value" << std::endl;
199 increment_by_value(i);
200 std::cout << "after function call, i = " << i << std::endl;
201
202 std::cout << "\nincrement by reference" << std::endl;
203 increment_by_reference(i);
204 std::cout << "after function call, i = " << i << std::endl;
205
206 std::cout << "\nincrement by pointer" << std::endl;
207 increment_by_pointer(&i);
208 std::cout << "after function call, i = " << i << std::endl;
209
210 // note, this will result in a compiler error
211 // increment_by_reference(i + 7);
212
213 // Create a vector containing doubles
214 std::vector<double> v = {7, 5, 16, 8}; // list initialized requires c++11 or later
215
216 // compute norm squared
217 std::cout << "norm squared: " << mutable_compute_norm_squared(v) << std::endl;
218 std::cout << "norm squared: " << const_compute_norm_squared(v) << std::endl;
219
220 // computing the area of a circle
221 std::cout << "\nArea of a circle (r = 1) with default args: \n"
222 << compute_area(1.0) << std::endl;
223 std::cout << "Area of a circle (r = 1) with print = true: \n"
224 << compute_area(1.0, true) << std::endl;
225
226 // call overloaded functions
227 int a = 2;
228 float b = 3;
229 double c = 9;
230
231 std::cout << "\nadd_numbers(int, int): (expect example 1) " << add_numbers(a, a) << std::endl;
232 std::cout << "add_numbers(double, double): (expect example 2) " << add_numbers(c, c) << std::endl;
233 std::cout << "add_numbers(double, float, int): (expect example 3) " << add_numbers(c, b, a) << std::endl;
234 std::cout << "add_numbers(int, double, float): (expect example 4) " << add_numbers(a, c, b) << std::endl;
235
236 // these will work because ints and floats can be promoted to doubles
237 std::cout << "add_numbers(float, flaot): (expect example 2) " << add_numbers(b, b) << std::endl;
238 std::cout << "add_numbers(float, int, int): (expect example 3) " << add_numbers(b, a, a) << std::endl;
239
240 // this will not work becasue we can't convert a float or double to an int (without casting first)
241 // compiler error
242 // std::cout << "add_numbers(double, double, double): " << add_numbers(c, c, c) << std::endl;
243
244 return 0;
245}
Excercises#
Problem 2 from Project Euler.