Session 2#

Agenda#

  1. C++ Branching and Loops

  2. C++ Functions

  3. 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.