## Overview The C++ programming language was born in the late 1970s and early 1980s. [Bjarne Stroustrup](https://en.wikipedia.org/wiki/Bjarne_Stroustrup) is the original designer and implementer: ![[Pasted image 20230103144839.png]] He wanted a language that was as efficient as [C](https://en.wikipedia.org/wiki/C_(programming_language)), but with better features for designing big programs. While C is great for low-level programming, such as interfacing directly with hardware, it can be difficult to use for higher-level applications. Similarly, while high-level languages like [Python](https://www.python.org/) or [JavaScript](https://en.wikipedia.org/wiki/JavaScript) are good at creating high-level applications, they're too inefficient for programs where performance is paramount (like operating systems, or graphics rendering engines). So, C++ is [Stroustrup's](https://en.wikipedia.org/wiki/Bjarne_Stroustrup) attempt at creating a language that is as efficient as C, but can also be used for high-level programs. Essentially, he borrowed the idea of classes and objects from the [Simula programming language](https://en.wikipedia.org/wiki/Simula) and added them to C. Classes let you create customized data types specific to the problem you're solving. C++ has been a very successful language. It has been under constant development since the early 1980s, with major new features still being added. It is a huge and complex language, and its not possible to understand *every* detail of it in a single course. We'll focus on a small but useful subset. One final note. While C++ supports high-level programming, its main usage is for programs that need low-level access to computer hardware, or where speed or memory-efficiency is key. ## C++ Review This course assumes you've already learned the basics of programming, and have a general understanding of things like variables, expressions, types, if-statements, loops, and functions. So here we give whirlwind review of some of the basics of C++. ## C++ is a Compiled Language C++ is a **compiled language**, which means that after we write C++ source code we pass it to a compiler that converts it to program we can run. Compiled languages contrast with **interpreted languages**, where there is no compiling step and the source code of the interpreted language is executed as it runs. Compiled languages tend to be more efficient than interpreted languages since they do not need to translate source code at run-time. However, interpreted languages tend to be easier to experiment with since they can easily run one statement or expression at a time. C++, C, Java, Rust, Go, and so on are examples of compiled languages. Python and JavaScript are examples of interpreted languages. In practice, many languages mix aspects of compilation and interpretation. For instance, Python and Java both do some compilation in an effort to boost their performance. ## Building C++ Programs In any C++ program the first function that is called is `main`. For instance, here is the contents of a C++ source code file called `hello_world.cpp`: ```cpp // hello_world.cpp #include <iostream> using namespace std; int main() { cout << "Hello, world!\n"; return 0; } ``` To run this you must first compile it. Your editor/IDE may have a button for this, but is more instructive to run the compiler directly from the command line. In a terminal command line you can compile it like this: ```bash $ c++ hello_world.cpp -o hello_world ``` `
is the command-line prompt, and you don't need to type it. On some computers the name of the compile might be `g++` instead of `c++`. After you type in the command you press enter/return, and that will create an executable file named `hello_world` that you can run like this: ```bash $ ./hello_world Hello, world! ``` Note that to run the program you must put `./` before it. This is essentially to tell the computer you want to run the `hello_world` file that's in this current folder. ### Using make We're going to be compiling a lot of programs, and it is easy to make a mistake if every time you have to type out a long command like `c++ hello_world.cpp -o hello_world`. So what we'll do instead is use the standard `make` program to simplify compiling: ```bash $ make hello_world c++ hello_world.cpp -o hello_world ``` `make` knows how to call the C++ compiler and automatically makes the compiling command for us. The C++ compiler has many options that can help us prevent bugs in our programs, and to turn them on we'll use a makefile: ![[course makefile]] To use it, copy the above into a file named `makefile` in the same folder as the `hello_world.cpp`. Then compile it with `make`: ```bash $ make hello_world c++ -std=c++17 -Wall -Wextra -Werror -Wfatal-errors -Wno-sign-compare -Wnon-virtual-dtor -fsanitize=address hello_world.cpp -o hello_world $ ./hello_world Hello, world! ``` This compiles `hello_world.cpp` using the given options, which can find various problems. In larger and more complex programs this extra checking is quite useful. A couple of things about `make`. First, if the program you are trying to make already exists then it won't re-compile it if the source code file has not changed: ```bash $ make hello_world make: `hello_world' is up to date. ``` This can save a lot of time when compiling large multi-file programs. If you want to *force* it to re-compile then use the `-B` option: ```bash $ make -B hello_world c++ -std=c++17 -Wall -Wextra -Werror -Wfatal-errors -Wno-sign-compare -Wnon-virtual-dtor -fsanitize=address hello_world.cpp -o hello_world ``` Second, `make` essentially provides and entire language for building C++ (and C and related languages) programs. It can do things like compile programs in a particular order, print messages, define functions, and so on. That's useful in larger programs, but for this course we will not go beyond the above simple use of `make`. ## C++ Program Basics It is useful to look at the basic structure of a C++ program. Consider `hello_world.cpp` again: ```cpp // hello_world.cpp #include <iostream> using namespace std; int main() { cout << "Hello, world!\n"; return 0; } ``` The first line is a source code comment, i.e. a note to the programmer. It is not necessary, and is there just as a convenience to make the name of the file clear. C++ has two styles of source code comments: - **`//`-style comments** begin with `//` and continue until the end of the line. They can only be a single line. - **`/* */`-style comments** begin with `/*` and continue until `*/`. They can span over multiple lines. The next line is `#include <iostream>`, which is a **pre-processor directive**. `#include` directives are replaced by the contents of the given file, and in C++ are used to import other code. Any program that wants to print to the terminal using `cout` or read from it using `cin` must include `<iostream>` as shown. Next comes `using namespace std`, which is a **using directive**. The code inside `<iostream>` is inside a [[Namespaces|namespace]] called `std`, and if you did not have the using directive you'd need to write `std::cout` instead of `cout`. Sometimes we will write `std::cout`, but often writing `std::` so much clutters the code and can make it harder to read. After the using directive is the `main` function. All C++ programs need a `main` function, and `main` is always the function that a C++ program calls first. Note that it's return type is `int`, i.e. `main` returns an `int`. By convention, the `int` that `main` returns is an error code that is sent to the operating system. Returning 0 from main means the program terminated normally; returning any other value means that the program terminated with an error. We will almost never need to use `main` return values in this course, and will often not write the final `return` in `main`. C++ uses `{` and `}` to mark blocks of code. For a function, `{` marks the beginning of the code body, and `}` marks the end. The code inside `{}` should be consistently indented to make it easier for humans to read (although the C++ compiler does not care about this indentation). In `main` there are two statements: `cout` statement that prints a message on the screen, and a `return` statement. Both are required tp end with a `;`, i.e. `;` marks the end of a statement. Finally, `"Hello, world!\n"` is an example of a **string literal**. In C++, basic string literals begin and end with `"`. Inside the string literal there is `\n`, which is an example of an **escape character**. `\n` indicates the **newline character**, which in the terminal makes the cursor go to the next line. In the source code we can't just press return because that would cause the cursor to go to the next line in the text editor. ### Reading from the Terminal with cin and getline You can read in a single word form the terminal using `cin`: ```cpp // hello_name_cin.cpp #include <iostream> // for cin and cout #include <string> // for string using namespace std; int main() { cout << "What's your name? "; string name; cin >> name; cout << "Hello, " << name << "!\n"; } ``` Here's a sample run: ``` $ make hello_name_cin c++ -std=c++17 -Wall -Wextra -Werror -Wfatal-errors -Wno-sign-compare -Wnon-virtual-dtor -fsanitize=address hello_name_cin.cpp -o hello_name_cin $ ./hello_name_cin What's your name? Bob Hello, Bob! $ ./hello_name_cin What's your name? Bob Ross Hello, Bob! ``` As you can see, `cin` only reads in the first word. If you want all the characters, use `getline`: ```cpp // hello_name_getline.cpp #include <iostream> // for getline and cout #include <string> // for string using namespace std; int main() { cout << "What's your name? "; string name; getline(cin, name); cout << "Hello, " << name << "!\n"; } ``` Then: ``` $ make hello_name_getline c++ -std=c++17 -Wall -Wextra -Werror -Wfatal-errors -Wno-sign-compare -Wnon-virtual-dtor -fsanitize=address hello_name_getline.cpp -o hello_name_getline $ ./hello_name_getline What's your name? Bob Hello, Bob! $ ./hello_name_getline What's your name? Bob Ross Hello, Bob Ross! ``` ## Types, Variables, and Values C++ is, for the most part, a [[statically typed language]]. That means that the *type* of values and functions is *known at compile-time*, and so the compiler can catch many type-related errors. Most of the time we will explicitly list the types of variables and values when they are declared. For example: ```cpp int a = 5; // the types of double x = 6.22; // a, x, and c char c = 'm'; // are explicitly given cout << a << ' ' << x << ' ' << c << ' ' << "\n"; ``` `a`, `x`, and `c` are variables. The code explicitly declares `a` to be of type `int`, `x` to be of type `double`, and `c` to be of type `char`. Since C++11, the `auto` keyword can be used to *infer* types in some situations. For example: ```cpp auto a = 5; // auto uses type inference auto x = 6.22; // to determine the types auto c = 'm'; // of a (and int), x (a double), and c (a char) cout << a << ' ' << x << ' ' << c << ' ' << "\n"; ``` For variable `a`, C++ infers that it is of type `int` because the literal value 5 is of type `int`. Similarly, it knows `x` is a `double` because the literal value 6.22 is of type `double` C++ has a large selection of standard low-level types for integers, characters, and floating point types. Note that C++ does not require that you give a variable an initial value when you define it: ```cpp int n; cout << n; // ??? ``` Here we've defined `n` but not given it an initial value. Depending on your compiler or editor, this may be flagged as an error, which it is a good thing! If you don't give `n` an initial value, it could get *any* possible `int`, and so we can't assume an uninitialized value has any particular value. This leads to all kinds of subtle and nasty errors, and many other languages simply outlaw such statements. ## Control Structures: Loops, If-statements, and Switches ### If-statements Here's the general form of a C++ if-statement: ```cpp if (cond_1) { body_1; } else if (cond_2) { body_2; } else if (cond_3) { body_3 } . . . } else if (cond_n) { body_n; } else { body_else; } ``` Each of `cond_1` to `cond_n` are *conditions*, i.e. boolean expressions that evaluate to either `true` or `false`. The body of the first true condition is executed, and then the flow of control jumps out of the if-structure to run the code that comes after it. The `else` parts are all optional. The final `else` has no condition in this case and is executed just when all the conditions above it are `false`. Here's an example of a program that uses an if-else statement to check the age of the user: ```cpp #include <iostream> using namespace std; int main() { int age; cout << "Please enter your age: "; cin >> age; if (age < 0) { cout << "Invalid age entered.\n"; } else if (age == 0 || age == 1) // || is logical or { cout << "You are an infant.\n"; } else if (age < 18) { cout << "You are a minor.\n"; } else if (18 <= age && age < 65) // && is logical and { cout << "You are an adult.\n"; } else { cout << "You are a senior citizen.\n"; } return 0; } ``` ### Switch Statements Switch statements make decisions on basic integral types like `char`s and `int`s, e.g.: ```cpp char sep = ','; switch (sep) { case ',': cout << "definitely a separator"; break; case '-': // minus sign case '_': // underscore cout << "maybe a separator"; break; default: cout << "not a separator"; } ``` The code under the cases is executed when the value matches the case. Notice that `break` *must* be used to stop the program from "falling through" to the next case. Each case in a `switch` statement is a possible value of the variable being switched on. If you want to check for a range of cases, then you must list each value explicitly with its own `case`: ```cpp int n = 3; switch (n) { case 0: case 2: case 4: case 6: case 8: cout << "even digit"; break; case 1: case 3: case 5: case 7: case 9: cout << "odd digit"; break; default: cout << "not a digit"; } ``` `default:` is the catch-all case. The code after it will be run if none of the cases above it are matched. C++ switch statements **do not** work with strings, or arrays, or other complex data types. Even though they are less flexible than if-statements, the compiler can sometimes compile switch statements into very efficient code (e.g. a "jump table") that is faster than an if-statement. ### While Loops A C++ while-loop has this general form: ```cpp before_code while (cond) { body_code } after_code ``` The condition `cond` is a boolean expression that evaluates to either `true` or `false`. If `cond` is true, then `body_code` is executed. After that finishes, `cond` is checked again, and, if it is `true`, `body_code` is executed again. If `cond` evaluates to `false`, then `body_code` is *not* executed, and the programs jumps to `after_code`. Note that if `cond` is initially `false`, then `body_code` is *never* run. In other words, the body of a while-loop runs 0, or more, times. Here's a while loop that prints the numbers 1 to 10: ```cpp int n = 1; while (n <= 10) { cout << n << "\n"; n++; // increment n by 1 } ``` And here's one that prints 10 to 1: ```cpp int n = 10; while (n >= 1) { cout << n << "\n"; n--; // decrement n by 1 } ``` ### Do-while Loops C++ do-while loops have this general form: ```cpp before_code do { body_code } while (cond); // note the semi-colon here! after_code ``` It is essentially the same as a while-loop, but the condition `cond` is check *after* `body` is executed. This means the body of a do-while loop is always executed at least once. > We will rarely, if ever, use do-while loops in this course. For most purposes, while-loops and for-loops are all we need. ### C-Style For-loop A C-style for-loop has this general form: ```cpp before_code for(init; cond; incr) { body_code } after_code ``` When the for-loop is executed, the first thing it does is run `init`. Then it checks to see if `cond` is `true`; if it's not, it immediately skips to `after_code`. But if `cond` is `true`, then it executes `body_code`, and then after that executes `incr` and checks the condition again. `init` and `incr` are often used with an index variable. For example, this prints the numbers from 1 to 100: ```cpp for(int i = 0; i < 10; i++) { cout << i + 1; } ``` C-style for-loops can be re-written as a while-loop, e.g.: ```cpp { int i = 0; while (i < 10) { cout << i + 1; i++' } } ``` In general, for-loops are preferred over while-loops since the put all the loop control information into one place at the top of the loop. ### For-each Style Loop Since C++11, C++ also a **for-each** style loop that directly loops through the elements of a vector or array without the need for an index variable. For example, this prints all the values of `nums`: ```cpp vector<int> nums = {5, 6, 3, -2, 0, 5}; for(int n : nums) { cout << n << "\n"; } ``` The `:` is read as "in", and so the loop is saying "for each of the `int`s `n` in `nums`, do the following ...". In general, it's usually a good idea to use for-each loops when you can. They are usually easier to read than equivalent for-loops or while-loops. ## Functions The C++ functions we'll mostly use have this general form: ```cpp T fun_name(T1 param_1, T2 param_2, ..., Tn param_n) { // ... C++ code ... // ... return statement that returns a value of type T ... } ``` This function is called `fun_name`, and it takes `n` input parameters of types `T1`, `T2`, ..., `Tn`. It returns one value of type `T`, and the function must end with a return statement of the form `return expr`. `expr` evaluates to a value of type `T`. The details of [[Calling a function|calling a function]] are useful to know. For instance, they help us better understand [[recursion]]. ### Passing Parameters to Functions We will consider two types of parameter passing in this course: [[pass by value]], and [[pass by reference]]. When parameters are **passed by value**, a *copy* of them is passed to the function, e.g.: ```cpp int f(int n) { // n is passed by value: a copy is made n++; return n + 2; } int main() { int a = 5; cout << f(a) << "\n"; // prints 7 cout << a << "\n"; // prints 5 (a is NOT changed) } ``` Note that the variable `a` is *not* changed in this example. **Pass by reference** passes the actual variable to the function *without* making a copy. There are two main reasons to use pass by reference: 1. **Pass by reference can save memory**. For example, if you pass a string to a function using pass by value, then the string will be copied. If the string is big, making the copy could take a significant time and memory. With pass by reference, no copy would be made of the string. 2. **Pass by reference lets you modify the value of the passed-in variable**. Compare this example to the one above: ```cpp int f(int& n) { // n is passed by reference: note the & n++; return n + 2; } int main() { int a = 5; cout << f(a) << "\n"; // prints 7 cout << a << "\n"; // prints 6 (a is changed) } ``` An important variation of [[pass by reference]] is [[pass by constant reference]]. [[Pass by constant reference]] passes a parameter by reference, but does *not* allow the function to modify it any way. In many cases, [[pass by constant reference]] is the best way to pass parameters because it is both efficient and avoids the problem of functions unexpectedly changing variables. For example, string `s` is passed by conference reference: ```cpp int count_spaces(const string& s) { int num_spaces = 0; for(char c : s) { if (c == ' ') num_spaces++; } return num_spaces; } ``` ### void Functions If a function doesn't return a value, then its return type should be `void`: ```cpp void fun_name(T1 param_1, T2 param_2, ..., Tn param_n) { // ... C++ code ... } ``` `return` statements are optional in `void` functions. You can put a `return;` (with no value returned) statement in a `void` function to make the function end immediately. If a `void` function doesn't have a return, then it ends when the code ends. In this program, `greet` is a `void` function: ```cpp #include <iostream> using namespace std; void greet(const string& name) { cout << "Hello, " << name << "!\n"; } int main() { cout << "What's your name? "; string name; cin >> name; greet(name); } ``` ### Local Variables A variable defined inside a function is called a [[local variable]]. For example, `num_spaces` is a local variable in the `count_spaces` function: ```cpp int count_spaces(const string& s) { int num_spaces = 0; // local variable for(char c : s) { if (c == ' ') num_spaces++; } return num_spaces; } ``` The passed-in parameter `s` is treated as if it were local to `spaces` as well, and so `count_spaces` has three local variables: `s`, `num_spaces`, and `c`. Local variables are created when their function is called, and then they are automatically deleted when their function ends. Code outside of the function cannot read or write the values of local variables. ## Includes and the Standard Namespace C++ comes with a large standard library of useful functions and classes. They are in a [[Namespaces]] called `std` (short for "standard"), and are spread across a series of header files that you can include. For example: ```cpp #include <iostream> int main() { std::cout << "Hello, world!\n"; // std:: is required! } ``` Because `cout` is in the `std` namespace we must access it using the prefix `std::`. To avoid cluttering our code with `std::`, we'll often use a `using` statement: ```cpp #include <iostream> using namespace std; int main() { cout << "Hello, world!\n"; // std:: no longer required } ``` `using` tells C++ to check the `std` namespace if it runs into a name that it doesn't know about. This lets write `cout` instead of `std::cout`. C++ `#include`s are plain textual inclusion: the `#include` is replaced by the contents of the file. While this is simple, it causes some problems. For example, the compile-times of large C++ program can be in the *hours*, in part due to the `#include` statement being so inefficient. C++20 has added *modules* which solve many of the problems of `#include`. If modules catch on, then `#include` statements may be phased out of C++ programs entirely. ## Structs One way to create your own data type in C++ is use a `struct`. For example: ```cpp #include <iostream> #include <string> using namespace std; struct Person { string name; int age; }; int main() { Person dad{"John", 30}; Person mom{"Jane", 28}; Person child{"Jim", 5}; cout << "Dad: " << dad.name << " " << dad.age << "\n"; cout << "Mom: " << mom.name << " " << mom.age << "\n"; cout << "Child: " << child.name << " " << child.age << "\n"; } ``` The `Person` struct acts like a new type of value. A `Person` stores both the name and age of somebody. As the code shows, you can create a new `Person` like this: ```cpp Peron dad{"John", 30}; ``` This statement defines and initializes a new variable called `dad` that stores the string `"John"` and the integer 30. You access the particular values inside a struct, using `.`-notation (*dot notation*), e.g.: ```cpp cout << dad.name; // prints "John" cout << "\n"; cout << dad.age; // prints 30 ``` Structs are like records in database: they store a group of related values. They have many uses in programming. And, as we will see, C++'s object-oriented features are extend structs to allow functions with them, and to allow them to be created using a trick called inheritance. ## Questions 1. What's the name of the original inventor and implementer of C++? 2. What programming language did C++ get the idea of objects and classes from? 3. Name two kinds of programs that C++ would probably be **good** for writing. Name two kinds of programs that C++ would probably **not be good** for writing. Justify your answers. 4. What's the difference between a compiled language and an interpreted language? 5. What does it mean when we say that C++ is a [[statically typed language]]? 6. Explain what the `auto` keyword does in C++. Give an example of how to use it. 7. What types of variables can a `switch` statement switch on? 8. In a `switch` statement, what happens if you don't end a case with a `break`? 9. In a `switch` statement, what is the label for the "catch-all" case? 10. In your own words, describe how **while-loops** and **do-while loops** work, and how they differ. 11. What is the general form of a **C-style** for-loop? 12. Give an example of how to use a **for-each** style loop. 13. In your own words, explain how **pass by reference** parameter passing works, and also how **pass by value** parameter passing works. When would you use each? 14. In your own words, explain [[pass by constant reference]] parameter passing. Give an example of where it could be used. 15. What is a **namespace**? What namespace is `cout` in? 16. Write a struct called `City_temp` that stores the name of a city, and the temperature of that city.