A Tour of C++ ============= In these notes we will look at a couple of small sample programs that demonstrate some of the basic features of C++. Throughout this course, we'll be using :download:`std_lib_cmpt125.h <_downloads/std_lib_cmpt125.h>` to simplify our programs. Put a copy of it in the same directory as your ``.cpp`` programs. :download:`std_lib_cmpt125.h <_downloads/std_lib_cmpt125.h>` includes a number libraries and functions that we will be using throughout the course. Example: Hello, World --------------------- The traditional first C/C++ program is one that prints "Hello, world!" on the screen. It demonstrates some of the basic syntax of C++:: // helloworld.cpp #include "std_lib_cmpt125.h" int main() { cout << "Hello, world!\n"; return 0; } Save this code --- *exactly* as it appears --- in a file called ``helloworld.cpp``. When the two files are ready, compile ``helloworld.cpp`` like this:: $ make helloworld g++ helloworld.cpp -o helloworld The ``make`` command automatically calls the ``g++`` C++ compiler to create an executable file called ``helloworld``. To run it type this:: $ ./helloworld Hello, world! Hello World Line by Line ------------------------ Lets go through the program line by line: - The first line is a **source code comment**:: // helloworld.cpp ``//`` marks the beginning of a source code comment that continues until the end of the line. - The next line is an **include**:: #include "std_lib_cmpt125.h" ``#include`` statements work by inserting the contents of the included file. In this case, the line ``#include "std_lib_cmpt125.h"`` gets replaced by the contents of the file :download:`std_lib_cmpt125.h <_downloads/std_lib_cmpt125.h>`. - The next line is the **header** for the main function:: int main() First is the return type, in this case ``int``. This means that ``main`` will **return** (i.e. evaluate to) a value of type ``int`` (i.e. an integer). Second is the name of the function: ``main``. After the name comes the list of input parameters, i.e. the inputs to the function. This particular function has no inputs, so we just write ``()``. ``main`` is a special function. All C++ programs must have exactly one function called ``main`` that returns an ``int``. A C++ program always calls ``main`` first. Be careful to spell ``main`` correctly. Case matters in C++, so ``Main``, ``MAIN``, and ``mAIn`` are all wrong. - After the function header comes an open brace:: { In C++, ``{`` marks the beginning of a **block of code**. Some programmers read it as "begin". The block of code under a function header is called the function's **body**. - This line prints "Hello, world!" on the console:: cout << "Hello, world!\n"; ``cout`` is a pre-defined object that stands for "character output". The ``<<`` operator indicates that the string on the right should be sent to the console as output. ``"Hello, world!\n"`` is a **string literal**. In C++, string literals always start and end with a double-quote mark ``"``. The ``\n`` is a special **escape character** called **newline**. When a newline is sent to ``cout`` it causes the cursor to go to the next line. Finally the line ends with a semi-colon, ``;``. In C++, a semi-colon is a **statement terminator**, i.e. it marks the end of a statement. - The final line of the function body is a **return statement**:: return 0; ``return`` does two things: it immediately ends the function, and it returns (in this case) the value 0. This is the value that the program returns to the operating system when it ends, and it may be useful if the program is called as part of a script. If the program doesn't terminate correctly, then it is customary to return a non-zero value to indicate what sort of error has occurred. Notice that 0 is an integer, and so is of type ``int``. The return-type declared in the function's header should be the same as the type of the value returned in the return statement. Otherwise, errors can result. For example, if you tried to return a string, such as ``"zero"``, you'd get an error message like this:: $ make helloworld g++ helloworld.cpp -o helloworld helloworld.cpp: In function ‘int main()’: helloworld.cpp:8:11: error: invalid conversion from ‘const char*’ to ‘int’ [-fpermissive] make: *** [helloworld] Error 1 - Finally, the program ends with a close brace:: } A ``}`` marks the end of a block of code, and it matches the open brace ``{`` at the top. All open and closed braces must be matched like this. Compiling and Linking --------------------- When you create an executable file with ``make helloworld``, a few things occur: 1. The C++ **compiler** is called on ``helloworld.cpp``. The compiler converts the **source code** (i.e. the contents of ``helloworld.cpp`` into object code. Code that doesn't follow the syntax rules for C++ is known as a **syntax error**, and usually causes a **compiler error**, aka **compile-time error**. 2. If the compiler ran without finding any errors, then it produces object code that is sent to a program called the **linker**. The linker combines object code from multiple files to create a single executable file. For example, the code for ``cout`` and ``<<`` is already compiled and stored as object code in a library file. Thus, you don't need to re-compile it every time you compile your program. Instead, the library object code is linked into your executable file. It's possible that linking could fail because your program calls a function that is not in any of the linked object code files. Such errors are called **linker errors**, or sometimes **link-time errors**. 3. If the linker runs without error, then it produces an executable file that can be run. If there are errors in the program that were not caught by the compiler or linker, they could cause your program to crash or behave badly in some way. Errors like this that occur while a program is running are known as **run-time errors**. Usually, we talk imprecisely and say that a program is being compiled when it is really being compiled *and* linked. Most of the time that presents no problems, but everyone once in a while bugs related to the linker can arise, and so it is important to understand that the linker exists. The CMPT 125 Header File ------------------------ As mentioned above, we'll be using the header file :download:`std_lib_cmpt125.h <_downloads/std_lib_cmpt125.h>` in most of our programs. It imports a number of standard libraries, and also defines some useful helper functions that we'll be using throughout the course. The ``#include`` statement is a simple textual include, i.e. it is replaced by the contents of the file it includes. So if you like typing you could delete the include statement and type in the contents of :download:`std_lib_cmpt125.h <_downloads/std_lib_cmpt125.h>`. The results would be the same. Example: What's Your Name? -------------------------- Let's write a program that asks the user for their name and age, and then says hello to them. It will work like this:: What's your name? May How many years old are you? 19 Hi May! A decade from now you'll be 29 years old! Here's a first draft that does what we want:: int main() { cout << "What's your name? "; string name; cin >> name; cout << "How old are you? "; int age; cin >> age; cout << "Hi " << name << "!\n" << "A decade from now you'll be " << (age + 10) << " years old.\n"; } // main Notice how we use ``cin`` to read input. The statement ``cin >> age`` knows to read an ``int`` because the variable ``age`` is of type ``int``. Nothing stops the user from entering a nonsensical age, such as 0 or -18. So to deal with that we can use an if-statement to catch non-positive ages:: // ... if (age <= 0) { cout << "Your age must be greater than 0!\n"; } else { cout << "Hi " << name << "!\n" << "A decade from now you'll be " << (age + 10) << " years old.\n"; } // ... This handles negative ages, but it doesn't handle the case when the user enters a non-integer for the age, e.g.:: What's your name? May How old are you? six Your age must be greater than 0! This is not a sensible error message, since the problem is that the user a non-integer. The way to handle this problem is to check to see if reading an ``int`` from ``cin`` was successful:: // ... cout << "How old are you? "; int age; cin >> age; if (!cin.good()) error("age must be an int"); // ... The expression ``cin.good()`` is ``true`` if the last operation on ``cin`` was successful. If the user enters a non-``int`` for the age, then ``cin >> age`` will cause ``cin.good()`` to be ``false``. In C++, ``!`` means "not", and so the expression ``!cin.good()`` is ``true`` just when ``cin.good()`` is ``false``. The ``error`` function is not a standard C++ function, but is instead defined in ``std_lib_cmpt125.h``. It causes the program to immediately crash with an error message. Example: Finding the Median --------------------------- In this example, we want to create a program that reads in a sequence of numbers, and then prints the *median* value of the numbers. Recall that the median of a set of numbers is the number that is in the middle when the numbers are examined in ascending sorted order. For example, the median of {6, 9, 2, 1, 4} is 4. Here's how we want our program to work:: Please enter some numbers: 6 9 2 1 4 median: 4 To signal the end of the input, we use an EOF (end-of-file) character by pressing ctrl-D in Linux, or ctrl-Z in Windows. Here's a program that does the calculation:: #include "std_lib_cmpt125.h" int main() { cout << "Please enter some numbers: "; vector nums; // nums initialized to be empty double x; // unknown initial value while (cin >> x) { nums.push_back(x); // append x to the end of nums } sort(nums.begin(), nums.end()); cout << "\nMedian: " << nums[nums.size() / 2] << endl; } // main A few notes: - ``nums`` is declared to be of type ``vector``, meaning it is a container that can hold ``double``\ s. Initially it is empty, i.e. it has 0 ``double``\ s in it. - The loop beginning ``while (cin >> x)`` repeats the block of code underneath it until a non-``double`` (such as the EOF character) is read in. - ``sort`` is a function in C++'s standard library that can be used to sort ``vector``\ s. By default, ``sort`` arranges the vector into ascending order. It turns out to be quite easy to also find the median of a list of words. The median of a list of strings is the word that would occur in the middle if the list were arranged in alphabetical order:: #include "std_lib_cmpt125.h" int main() { cout << "Please enter some words: "; vector words; // words initialized to be empty string s; // unknown initial value while (cin >> s) { words.push_back(s); // append x to the end of words } sort(words.begin(), words.end()); cout << "\nMedian: " << words[words.size() / 2] << endl; } // main Of course, it's not immediately obvious why it would ever be *useful* to calculate the median of a set of words, but the point of this example is to demonstrate how easy it is to do some basic string processing in C++. Example: Generating Random Passwords ------------------------------------ Suppose you need to generate random passwords. One way to do this is to choose a set of symbols you want to appear in the passwords, and then to choose a sequence of those symbols at random. So we'll start by declaring a ``string`` that contains all the legal symbols:: const string legal_symbols = "abcABC123!\":"; This statement declares a variable named ``legal_symbols`` of type ``string``. It's also declared ``const``, which means that the compiler won't let ``legal_symbols`` be changed (all we can do is *read* ``legal_symbols``). In general, it's a good idea to declare your variables to be ``const`` whenever possible, as it can help you find errors, and sometimes may even speed up your program. We initialize ``legal_symbols`` to be this **string literal**:: "abcABC123!\":" In C++, string literals begin and end with a ``"`` character. If you want a ``"`` to appear as character in the string itself then you need to write it as ``\"`` as we've done. Next, a password is a sequence of random characters chosen from ``legal_symbols``, and so we'll make a function that returns one random symbol:: char rand_symbol() { int n = legal_symbols.size(); int r = randint(n); // 0 <= randint(n) < n return legal_symbols[r]; } This function is named ``rand_symbol``, and it does not take any inputs. It returns a value of type ``char``. The expression ``legal_symbols.size()`` returns the number of characters in ``legal_symbols``, and the ``randint`` function is from ``std_lib_cmpt125.h``. The expression ``legal_symbols[r]`` returns that character at index location ``r`` of ``legal_symbols``. Now that we can get one random symbol, lets write a function to generate a random password of some given length:: string rand_password(int n) { string s; for(int i = 0; i < n; ++i) { s += rand_symbol(); } return s; } The name of this function is ``rand_password``, and it takes one input parameter of type ``int`` called ``n``. It returns a ``string`` that contains ``n`` random characters. The statement ``s += rand_symbol()`` appends the character returned by ``rand_symbol()`` to the (right) end of ``s``. It's inside a for-loop that executes ``n`` times, and so we end up with a string of exactly ``n`` random characters. Here's the complete program:: #include "std_lib_cmpt125.h" // characters that can appear in a password const string legal_symbols = "abcABC123!\":"; // Returns one random symbol. char rand_symbol() { int n = legal_symbols.size(); int r = randint(n); // 0 <= randint(n) < n return legal_symbols[r]; } // Returns a string of n random symbols. string rand_password(int n) { string s; for(int i = 0; i < n; ++i) { s += rand_symbol(); } return s; } int main() { randomize(); // ensure a different random number // seed each time the program run cout << rand_password(5) << endl; } // main And here are a couple of sample runs:: $ make rand_passwords $ ./rand_passwords 1c"2a $ ./rand_passwords bB3CA $ ./rand_passwords !cCab Questions --------- #. What does this code print? :: cout << "a\nb\n" << "c" #. What does ``//`` mean in a C++ program? #. What is the return-type of ``main``? #. What is the input to a compiler? What is the output of a compiler? #. What is the input to a linker? What is the output of a linker? #. Give an example of a compile-time error. #. Give an example of a run-time error. #. Show how ``cin.good()`` can be used in a fragment of code to test if an ``int`` has successfully been read from ``cin``. Programming Questions --------------------- #. Modify the median program to also print the minimum value and the maximum value of the data entered.