├── 1. Linux.md ├── 10. Advanced C++.md ├── 11. Vtables and Multiple Inheritance.md ├── 2. Software Testing.md ├── 3. Basic C++.md ├── 4. Object Oriented Programing.md ├── 5. Encapsulation and Introduction to Design Patterns.md ├── 6. Relationships and Inheritance.md ├── 7. The Standard Template Library.md ├── 8. Error Handling with Exceptions.md ├── 9. Design Patterns.md ├── README.md └── images ├── 1-1.png ├── 1-2.png ├── 10-1.png ├── 11-1.png ├── 11-2.png ├── 11-3.png ├── 11-4.png ├── 11-5.png ├── 11-6.png ├── 4-1.jpg ├── 4-2.jpg ├── 6-1.png ├── 6-10.png ├── 6-11.png ├── 6-12.png ├── 6-12b.png ├── 6-13.png ├── 6-14.png ├── 6-15.png ├── 6-16.png ├── 6-17.png ├── 6-18.png ├── 6-19.png ├── 6-2.png ├── 6-20.png ├── 6-3.png ├── 6-4.png ├── 6-5.png ├── 6-6.png ├── 6-7.png ├── 6-8.png ├── 6-9.png ├── 9-1.jpg ├── 9-1.png ├── 9-10.jpg ├── 9-11.jpg ├── 9-12.jpg ├── 9-13.jpg ├── 9-2.png ├── 9-3.png ├── 9-4.jpg ├── 9-4.png ├── 9-5.png ├── 9-6.png ├── 9-7.jpg ├── 9-8.png └── 9-9.jpg /1. Linux.md: -------------------------------------------------------------------------------- 1 | # 1. Linux 2 | 3 | 4 | 5 | 6 | 7 | ## 1.1 The Teaching Environment 8 | 9 | ### SSH 10 | 11 | SSH is a cryptographic network protocol that provides secure (encrypted) connections between a client (i.e. your computer) and a server (i.e., the School's Ubuntu server). 12 | 13 | When connected to the school server using SSH, the commands are executed in the server, not on local computer. Only the output of the command is transmitted back to your computer and shown in a terminal window. 14 | 15 | 16 | 17 | ### git: a Version/Revision Control System 18 | 19 | - `git` is a popular version/revision control system that was developed by Linus Torvalds. 20 | - it has become somewhat of an industry standard 21 | - you get a local repository that are free to modify, allowing you to rollback changes, branch code, etc. This functionality is not available on SVN(Subversion)/CVS. 22 | 23 | 24 | 25 | ### Copying files 26 | 27 | Use the `scp` command. 28 | 29 | The core `scp` format is 30 | 31 | ```bash 32 | scp source destination 33 | 34 | each of the source/destination can be of the form: 35 | [[user@]host1:]file1 36 | ``` 37 | 38 | 39 | 40 | - you can use SSHFS to mount your student account as a drive (can view the file in a Finder Window) 41 | 42 | 43 | 44 | ### Zipping Files 45 | 46 | - you should ensure you have zipped the correct files by copying them to a clean directory and `unzip` it. 47 | 48 | - Syntax: 49 | 50 | ```bash 51 | zip [options] zipfile files_list 52 | 53 | Syntax for creating a zip file: 54 | zip myfile.zip filename.txt 55 | ``` 56 | 57 | 58 | 59 | ### Miscellaneous 60 | 61 | - press on on the up arrow `↑` to see previous commands you have entered 62 | - press the Ctrl-r key to search backwards through the bash history 63 | - the command `clear` will clear the terminal 64 | - When typing a command or a file name, press `tab` to autocomplete the word if the remainder of the word is not ambiguous (unique) 65 | 66 | 67 | 68 | ### Modules for this course 69 | 70 | - Linux Shell 71 | - C++ 72 | - Tools 73 | - Software Engineering 74 | 75 | 76 | 77 | ## 1.2 Interacting with the Shell 78 | 79 | ### Linux Shell 80 | 81 | **Shell**: a program that runs in computer and provides a user interface to the **OS**, get the OS to "do" things 82 | 83 | OS: **operating system**, the software running on your machine that makes it interactable for human (OS is what really "do" things for you on your computer, e.g. run programs, manage files) 84 | 85 | Two kinds of shells: 86 | 87 | - Graphical shell - **(GUI)** graphical user interface (typically interacted through click/touch) 88 | - Command line Shell - **(CLI)** command-line interface (type commands in; gives more ease in certain versatility) 89 | 90 | In this course we are going to use a specific shell, "**bash**" (Bourne Again Shell), this is a command-line shell. 91 | 92 | The shell manages user input and output and interprets and executes commands. 93 | 94 | ![image-20220120210336970](https://github.com/kila097/CS246_Notes/blob/main/images/1-1.png) 95 | 96 | Check what shell you're running: 97 | 98 | ```bash 99 | $ echo $0 100 | 101 | Output: 102 | -bash 103 | ``` 104 | 105 | 106 | 107 | ### Standard Input and Output 108 | 109 | There are three standard input and output devices used by the shell: 110 | 111 | 1. **Standard Input** `stdin`: the shell is reading text input (typically from a keyboard) 112 | 2. **Standard Output** `stdout`: the shell is writing text output (typically writes to a monitor) 113 | 3. **Standard Error** `stderr`: the shell is writing text output similar to *stdin*, but outputs error messages. By default, both output devices write to the same place (the terminal or monitor), but we can *redirect* them to different places (`2>`). Advantage of doing this is that error messages do not clutter the output files and corrupt formatting. Also, stdout may be *buffered*, meaning that the system may wait to accumulate output before actually displaying (*flushing*) it. stderr is *never buffered*, so error messages are displayed immediately. 114 | 115 | EVERY program executed on your OS is attached to *three streams*. 116 | 117 | ```mermaid 118 | graph LR; 119 | stdin-->Program 120 | Program-->stdout 121 | Program-->stderr 122 | 123 | 124 | ``` 125 | 126 | ```bash 127 | $ myProgram arg1 arg2 < inputfile.txt > outputfile.txt 2> logerror.txt 128 | ``` 129 | 130 | 131 | 132 | ### How to use the Shell 133 | 134 | 135 | 136 | #### The File System 137 | 138 | The files on computers are organized into **directories** (also known as **folders**). 139 | 140 | A **path** is the general name of a file or directory in a textual format. 141 | 142 | The character `/` is used as the delimiter between directory and file names. 143 | 144 | 145 | 146 | #### Absolute and Relative Paths 147 | 148 | **Root** is the starting point of the file system, and is denoted only by `/`. **root** is at the top of your directory hierarchy. 149 | 150 | All paths are somehow relative to the root directory. 151 | 152 | - any path that begins with `/` is an **absolute path** because it clearly identifies where the file or directory is in the file system (contains the root directory) 153 | 154 | ```bash 155 | / -> the root of the file system 156 | /file.txt -> a file named file.txt in the root directory 157 | /etc/ -> a directory 158 | /usr/share/dict/words/file.txt -? a file in the directory /usr/share/dict/words 159 | ``` 160 | 161 | - any path without a preceding `/` is a **relative path**, it is always *relative* to the current directory (or working directory). The shell always keeps track of what is the current directory. 162 | 163 | - a dot `.` identifies the *current* directory, two dots `..` identifies the *parent* of the current directory 164 | 165 | ```` 166 | file.txt -> a file named file.txt in the current directory 167 | subdir/abc/file.txt -> subdir must be in the current directory 168 | ./script.sh -> a file named script.sh in the current directory 169 | ../bin/script.sh -> a file named script.sh in the bin directory, which must be in the parent of the current directory 170 | ```` 171 | 172 | 173 | 174 | commands in bash are typically names of a program you want to execute 175 | 176 | Types of commands: 177 | 178 | - **Scripts** are text files that contain commands in a specific programming language. They are not executed directory by the operating system, but read by a program that interprets and executes the commands (the interpreter) (e.g., bash - works as a script interpreter, python, php, etc.) 179 | - **Programs** are files that contain commands in *binary format*. The file contents are sequences of bytes that are meant to be understood by the operating system, not by a human. Therefore, OS directly executes a program, without needing an interpreter. Usually, a program is written in a specific programming language and then *compiled* (e.g., C++ programs) 180 | 181 | 182 | 183 | - #### Cat 184 | 185 | Reads the contends of stdin and writes it to stdout. The input of `cat` can be **redirected** to read from and write to files. 186 | 187 | Displays the content of a file 188 | 189 | ```bash 190 | $ cat /usr/share/dict/words 191 | ``` 192 | 193 | 194 | 195 | - #### pwd 196 | 197 | Stands for "print working directory" - prints out the path to the current working directory (display the current directory) 198 | 199 | ```bash 200 | $ pwd 201 | Output: 202 | /u9/y287yuan 203 | ``` 204 | 205 | 206 | 207 | - #### cd 208 | 209 | changes the current directory 210 | 211 | ```bash 212 | $ cd 213 | cd .. -> go to parent directory 214 | cd . -> to current directory (no change) 215 | cd -> return to root directory 216 | ``` 217 | 218 | 219 | 220 | The **home directory** is a directory owned by the user, containing personal files without disturbing other users' files. 221 | 222 | ```bash 223 | ~/cs246 -> ~ replaces the absolute path of home directory 224 | this is still an absolute path, the same as saying 225 | /u9/y287yuan/cs246 226 | ``` 227 | 228 | 229 | 230 | Summary of Directories: 231 | 232 | | Directory | Meaning | 233 | | --------------------------------------------------- | --------------------------- | 234 | | . | Current directory | 235 | | .. | Parent of current directory | 236 | | ~ | Home directory | 237 | | / | Root directory | 238 | | Starts with / or ~ | Absolute path | 239 | | Does not start with / or ~ (may start with . or ..) | Relative path | 240 | 241 | 242 | 243 | 244 | 245 | #### The $PATH Variable 246 | 247 | A global system variable named **$PATH** that specifies where the shel should file executable files. 248 | 249 | The contents of the $PATH are a list of pathnames separated by the colon character `:`. 250 | 251 | ```bash 252 | $ echo $PATH 253 | >>> /home/gustavo/bin:/usr/local/bin:/usr/bin 254 | ``` 255 | 256 | Any command that contains a file name will look for executable files in those directories only. 257 | 258 | Use a relative path or absolute path to look for files not in the $PATH directories: 259 | 260 | ```bash 261 | $ myprogram -> looks for file named myprogram in one of the directories in the $PATH system variable 262 | $ ./myprogram ->looks for myprogram in the current directory 263 | ``` 264 | 265 | You can add a frequently used directory to teh $PATH variable (without removing the current contents of the variable): 266 | 267 | ```bash 268 | export PATH=${PATH}:/program/path 269 | ``` 270 | 271 | 272 | 273 | #### File Permissions 274 | 275 | - #### ls 276 | 277 | Lists (non-hidden) files and directories in the current directory 278 | 279 | ```bash 280 | ls (non-hidden files) 281 | ls -a (lists all files, including hidden files) 282 | ls -l 283 | ls -f 284 | ``` 285 | 286 | `ls -a` lists *all* files (including hidden) in the current directory. Hidden file is any file whose name begins with a period `.`(e.g., .bashrc, .bash_history, .aliases) 287 | 288 | `ls -l` gives a *long form* listing, which displays the file permissions and other information 289 | 290 | ```bash 291 | -rw-r----- 1 y287yuan cs246 42 Jan 21 04:29 abc.txt 292 | type/perms owner group size modified name 293 | ``` 294 | 295 | - **type**: `-` for an ordinary file; `d` for a directory 296 | - **permissions**: three groups of three bits 297 | - **owner**: the ID of the user that owns the file, usually the user who created the file, ownership can be transferred to another user (using command `chown`) 298 | - **group**: A user can belong to one or more groups. A file can be associated with one group. When a user and the file are in the same group, the group permissions are in effect for that user. 299 | - **size**: the size of the file in bytes 300 | - **modified**: the date and time when the file was last modified 301 | - **name**: the file name 302 | 303 | 304 | 305 | Each file or directory has a set of three types of permissions: user, group, and other: 306 | 307 | - **user permissions**: apply to the file's own user 308 | - **group permissions**: apply to members of the file's group (other than the owner) 309 | - **other permissions**: apply to everyone else 310 | 311 | 312 | 313 | Each of these three types may contain three specific permissions: Read, Write, and eXecute: 314 | 315 | | Bit | Meaning for ordinary files | Meaning for directories | 316 | | ----- | -------------------------------------------------------- | ------------------------------------------------------------ | 317 | | **r** | file's contents can be *read* | directory's contents can be *read* (e.g., ls, globbing, tab completion) | 318 | | **w** | file's contents can be *modified* | directory's contents can be *modified* (can add/remove files) | 319 | | **x** | file's contents can be *executed* as a program or script | directory can be *navigated into* (i.e., can `cd` into the directory) | 320 | 321 | Note: If a directory's execute bit is not set, there is no access at all to the directory, no matter how the other bits are set. 322 | 323 | The permissions are displayed by `ls` are the nine characters that follows the type bit, in the format `rwxrwxrwx`, first 3 are user permissions, middle 3 are group permissions, and last 3 are other permissions. 324 | 325 | If a permission is not set, then a `-` is displayed instead. 326 | 327 | 328 | 329 | To change a file permission, use the command `chmod ` 330 | 331 | `` consists of three parts: 332 | 333 | 1. user types: u(user/owner), g(group), o(other), or a(all) 334 | 2. operator: +(add permission), -(subtract permission), or =(set permission exactly) 335 | 3. permissions: r(read), w(write), and/or x(execute) 336 | 337 | 338 | 339 | 340 | 341 | #### Other Commands 342 | 343 | - #### dos2unix 344 | 345 | `dos2unix` will turn a non-valid Linux text file into an valid Unix Linux text file. 346 | 347 | 348 | 349 | - #### Ctrl+C `^C` 350 | 351 | Ctrl+C will forcibly stop the executing program , this *kills* the program if any programing is going on for too long. 352 | 353 | 354 | 355 | - #### Ctrl+D `^D` 356 | 357 | Ctrl+D will end a program from running, such as when we just typed `cat` and want to end the input. 358 | 359 | This is a special character known as **EOF** (end-of-file), it signals that the input has ended. 360 | 361 | 362 | 363 | - #### man 364 | 365 | Opens up the manual page to the command following `man`. 366 | 367 | `man ` 368 | 369 | 370 | 371 | What happens if we **just type cat**, no files after it? 372 | 373 | \- Nothing will appears to happen, but `cat` is waiting to read input. So we can type and send input to cat, it will print out everything we type just like it printed out the contents of the file. It is printing out the contents of the input, instead of printing out the contents of a file. 374 | 375 | ```bash 376 | $ cat 377 | In:Hello 378 | Out:Hello 379 | In:There 380 | Out:There 381 | ``` 382 | 383 | 384 | 385 | It could be useful if we can capture the output of `cat` into a file. Which we can. 386 | 387 | ```bash 388 | $ cat > output.txt 389 | any output from the cat command will be piped into output.txt 390 | ^D 391 | ``` 392 | 393 | To stop providing input, use `^D`, NOT `^C`, `^C` kills a program that's taking too long to execute, doesn't allow that program to exit gracefully, this program(`cat`) isn't taking too long, it's simply waiting for input. 394 | 395 | Hence, we use `^D` to signal "**End-of-File(EOF)**" to the program. 396 | 397 | 398 | 399 | --- 400 | 401 | ### Redirecting input/output 402 | 403 | In general: 404 | 405 | ```bash 406 | $ command args > file 407 | ``` 408 | 409 | Executes "command args", and captures the output in file, instead of sending it to the screen. This is called **output redirection**. Note that the OPERATING SYSTEM is the one moving the output to the file, the program is still just asking to print to the screen. 410 | 411 | 412 | 413 | We can also redirect input: 414 | 415 | ```bash 416 | $ cat < inputfile.txt 417 | it will print the contents of inputfile.txt to screen 418 | ``` 419 | 420 | This prints the same as `$ cat inputfile.txt` but something very **different** is happening. 421 | 422 | In the command `$ cat inputfile.txt`, the OS is running the program `cat`, and passing to that program the argument, which is the string "inputfile.txt", the program `cat` then chooses to open the file "inputfile.txt" because it received that name as a *command-line argument*, and then `cat` reads from the file. The code here that is written and executed in `cat` is code for reading from a file. 423 | 424 | When we run the command `$ cat < inputfile.txt`, this is actually the program cat running with **no arguments**. This is just like when we typed the command `$ cat` , and then typed into the keyboard. The program `cat` sees no arguments and asks to *read from input* into the keyboard. However, since we are **redirected** input to the file inputfile.txt, instead of asking the user to type in the keyboard, the **operating system** instead reads the contents of "inpuefile.txt" to `cat` as the input. The OS did the redirection before cat is executed. 425 | 426 | 427 | 428 | ```bash 429 | $ cat < inputfile.txt aTextfile.txt 430 | it will print the contents of aTextfile.txt to screen 431 | ``` 432 | 433 | This is because if cat has a command-line argument, it **won't** read from stdin. 434 | 435 | ```bash 436 | $ cat < inputfile.txt aTextfile.txt - 437 | it will print both the contents of aTextfile.txt and inputfile.txt to screen (aTextfile.txt first then inputfile.txt) 438 | 439 | -- we can switch the order of '-' 440 | $ cat < inputfile.txt - aTextfile.txt 441 | it will print both the contents of inputfile.txt and aTextfile.txt to screen (inputfile.txt first then aTextfile.txt) 442 | ``` 443 | 444 | The hyphen `-` as a cmd-line argument tells cat: please print also the content read from standard input. 445 | 446 | 447 | 448 | 449 | 450 | --- 451 | 452 | #### Command-line Arguments & Difference Between Arguments and Input Redirection 453 | 454 | Pass arguments to a program or script by writing each argument separated by a space after the command name 455 | 456 | ```bash 457 | $ cat -n file.txt (-n tells cat to prefix each line with a number) 458 | ``` 459 | 460 | Cat receives the filename as an *argument*, it will look for the file, open the file and read its contents instead of reading from stdin. 461 | 462 | ```bash 463 | $ cat -n < file.txt 464 | ``` 465 | 466 | The OS will open the file and redirect its content into cat's stdin. Cat will not receive the file name and will *only* read from stdin. 467 | 468 | 469 | 470 | - #### wc 471 | 472 | `wc` stands for word count, it tells <# of lines> <# of words> <# of characters> 473 | 474 | Usage: 475 | 476 | ```bash 477 | $ wc -c print the byte counts 478 | $ wc -m print the character counts 479 | $ wc -l print the newline counts 480 | $ wc -w print the word counts 481 | $ wc -L print the maximum display length of a line 482 | ``` 483 | 484 | 485 | 486 | If we `wc` two of more files, it will output the total word count of all files. 487 | 488 | Examples: 489 | 490 | ```bash 491 | $ wc output.txt 492 | Output: 493 | 5 22 106 output.txt 494 | 495 | $ wc output.txt someFile.txt 496 | Output: 497 | 5 22 106 output.txt 498 | 0 0 0 someFile.txt 499 | 5 22 106 total 500 | 501 | $ wc 502 | dskljlkdj 503 | sdsjdlkjs sdksjdl sd 504 | sdssds 505 | 3 5 38 506 | ``` 507 | 508 | Difference when using redirection: 509 | 510 | ```bash 511 | $ wc output.txt -wordcount knows it is reading a file [this is a command-line argument] 512 | 5 22 106 output.txt 513 | $ wc < output.txt -wordcount believes it is reading from input [this is not] 514 | 5 22 106 515 | *The operating system is giving the input from the file to wc 516 | ``` 517 | 518 | 519 | 520 | YOU CAN'T `$ echo < output.txt` because `$ echo` **does not read from stdin**, it only takes arguments. 521 | 522 | ```bash 523 | $ echo a file.txt -> echo just prints its argument back to stdout 524 | a file.txt 525 | $ echo a < file.txt -> stdin was redirected to file.txt, but echo does not read from stdin, so input was not used 526 | a 527 | ``` 528 | 529 | 530 | 531 | 532 | 533 | Many (but not all) commands accepts both forms of input. 534 | 535 | You can do both input and output redirection at the same time! 536 | 537 | ```bash 538 | $ cat < inputfile.txt > outputfile.txt 539 | cat takes input from inputfile.txt and put the output into file outputfile.txt 540 | (cat reads from stdin, which the OS feeds to it from inputfile.txt. cat prints out the input to the screen, and the OS redirects it to outputfile.txt) 541 | 542 | this behaviours similar to cp 543 | $ cp inputfile.txt outputfile.txt [this is taking command line arguments] 544 | This will copy the cotents from inputfile.txt to outputfile.txt 545 | ``` 546 | 547 | 548 | 549 | ```bash 550 | $ cat -n < inputfile.txt > outputfile.txt 551 | ``` 552 | 553 | NOTE! `-n` here is a command line argument, while inputfile.txt is the file to redirect input from, and outputfile.txt is the file to redirect output to. NEITHER of those are command line arguments. (`<>` are also not arguments because they are asking the OS to redirect I/O) 554 | 555 | This command has effectively copied inputfile.txt to outputfile.txt with line numbers attached. 556 | 557 | 558 | 559 | NOTE! Be careful when using the same file for redirecting I/O. `cat < thisfile.txt > thisfile.txt` this will overwrite thisfile.txt to an empty file. This is because when thisfile.txt is being redirected for output, the system will treat it as an empty file (output redirection will overwrite the file). So cat will read an empty file and then redirect it to an empty file. This is a bad idea. 560 | 561 | `$ cat thisfile.txt > thisfile.txt` will also empty the file, because when the program runs, the OS will empty the file to redirect the output (overwrites it). This is NOT a left to right things, if the output is redirected, the operating system will make it empty to begin with. 562 | 563 | 564 | 565 | #### Quoting Arguments 566 | 567 | The shell treat white spaces as separators between arguments. Use quotes `" ", ''`to pass a single argument that contains spaces. 568 | 569 | The single quotes `' '` will not interpolate anything (e.g., variables), whereas double quotes `" "` will: 570 | 571 | ```bash 572 | $ echo a b c -> three separate arguments 573 | a b c 574 | $ echo "a b c" -> a single argument 575 | a b c 576 | ``` 577 | 578 | ```bash 579 | $ echo "My shell is $0" -> double quotes replaces $0 by its value 580 | My shell is -bash 581 | $ echo 'My shell is $0' -> no variable substitution 582 | My shell is $0 583 | ``` 584 | 585 | 586 | 587 | 588 | 589 | 590 | 591 | *** 592 | 593 | ### Pipes 594 | 595 | Pipes `|` allow us to use the output of one program as the input to another by connecting the second program's stdin to the first program's stdout. 596 | 597 | ![image-20220120235208647](https://github.com/kila097/CS246_Notes/blob/main/images/1-1.png) 598 | 599 | 600 | 601 | Example 1: How many words occur in the first 20 lines of myfile.txt? 602 | 603 | ```bash 604 | $ head -20 myfile.txt | wc -w 605 | ``` 606 | 607 | There is no limit on how you can combine piping and I/O redirection. 608 | 609 | ```bash 610 | $ cat file.txt | head -20 | wc -w > words.txt 611 | ``` 612 | 613 | 614 | 615 | 616 | 617 | Example 2: Suppose words1.txt and words2.txt contain lists of words one per line, print a duplicate free list of all words that occur in any of these files 618 | 619 | ```bash 620 | $ cat words*.txt | sort | uniq 621 | uniq will omit repeated line (lines need to be adjacent to each other (i.e., sorted)) 622 | ``` 623 | 624 | 625 | 626 | We can also use the output of one program as the ARGUMENT to another program. 627 | 628 | A **subshell** creates another bash shell to execute a command within a command. We create subshell with the syntax `$(command)`. 629 | 630 | Here, the shell executes the commands date and whoami, and substitutes the results into the command line 631 | 632 | ```bash 633 | $ echo "Today is $(date) and I am $(whoami)" 634 | Today is Thu 20 Jan 2022 10:41:39 PM EST and I am y287yuan 635 | $ echo 'Today is $(date) and I am $(whoami)' 636 | Today is $(date) and I am $(whoami) 637 | 638 | - substitution only occurs inside double quotes, never inside single quotes 639 | ``` 640 | 641 | Notice the difference between using double quotes and single quotes. 642 | 643 | It is important that we used double quotes if we want this to be one string. Using single quotes will give you the literal string, single quotes do not allow *any substitution* to occur within them. BUT, important to note, both quotes suppress globbing pattern substitution. 644 | 645 | ```bash 646 | $ echo "*" 647 | * 648 | $ echo '*' 649 | * 650 | $ echo * 651 | -> prints out all files/directories in the current directory 652 | ``` 653 | 654 | 655 | 656 | ```bash 657 | $ echo Today is $(date) and I am $(whoami) 658 | Today is Thu 20 Jan 2022 10:41:39 PM EST and I am y287yuan 659 | ``` 660 | 661 | Here echo is receiving 12 arguments while in the first command (output of $(date) becomes 6 command line arguments to echo), echo is receiving one argument. The behavior is the same. You may want to give a string as a single argument to a program. 662 | 663 | Note that the substitution with the subshells happen BEFORE echo ever runs. Bash runs date and whoami, substitutes them in the output, and *then* runs echo with the substituted contents. 664 | 665 | 666 | 667 | We can combine the use of echo and cat to output the contents of a file. 668 | 669 | However, cat *preserves the original file format* while echo changes the whitespace of the text 670 | 671 | ```bash 672 | $ cat helloworld.txt 673 | hello 674 | world 675 | 676 | $ echo | cat helloworld.txt 677 | hello 678 | world 679 | 680 | $ echo $(cat helloworld.txt) 681 | hello world 682 | ``` 683 | 684 | Use the subshell `$( )` to tell the shell to execute the cat command before passing the result as command-line arguments to echo 685 | 686 | 687 | 688 | 689 | 690 | --- 691 | 692 | ### Globbing Pattern 693 | 694 | The shell can automatically expand a *few wildcard patterns* to match all the files that satisfy the pattern. This is known as **globbing** pattern. 695 | 696 | 697 | 698 | ```bash 699 | $ cat *.txt 700 | ``` 701 | 702 | The syntax `*.txt` is what is called a *globbing* pattern. In the context of globbing patterns, the wildcard character `*` means "match *any* sequence of characters", when the shell encounters a globbing pattern, it will find all (non-hidden) files in the current-directory that fit the pattern and substitutes them onto the command line. 703 | 704 | The above command line might be replaced with: 705 | 706 | ```bash 707 | $ cat abc.txt bcd.txt cde.txt 708 | ``` 709 | 710 | This is where cat gets it name (con**cat**enate) 711 | 712 | 713 | 714 | The following operators are used in globbing patterns: 715 | 716 | | Operator | Meaning | 717 | | -------- | ------- | 718 | | * | matches **0 or more** characters | 719 | | ? | matches **1** character | 720 | | [abc] | matches **exactly one** of the characters in the brackets | 721 | | [!abc] | matches any character **except** the ones in the brackets | 722 | | [a-z] | matches any character in the given range | 723 | | {pat1, pat2} | matches either pat1 or pat2 (note no spaces) | 724 | 725 | 726 | 727 | 728 | 729 | 730 | 731 | 732 | ## 1.3 Regular Expressions 733 | 734 | 735 | 736 | ### Pattern-Matching in Text Files 737 | 738 | `egrep` stands for Extended global regular expression print, it looks for lines that match a pattern. 739 | 740 | `egrep `: This will print every line in file that matches pattern. 741 | 742 | The patterns that `egrep` understands are called *regular expressions*, and are NOT the same as globbing patterns, and more powerful. 743 | 744 | 745 | 746 | Example 1: print every line in alice.txt that contains Rabbit 747 | 748 | ```bash 749 | $ egrep "Rabbit" alice.txt 750 | ``` 751 | 752 | 753 | 754 | egrep is *case-sensitive* 755 | 756 | Example 2: print every line in alice.txt that contains "Rabbit" or "rabbit" 757 | 758 | ```bash 759 | $ egrep "Rabbit|rabbit" alice.txt -> here "|" means "or", the pipe does not have its meaning from bash, this is a string. 760 | 761 | $ egrep "(R|r)abbit" alice.txt 762 | $ egrep "[rR]abbit" alice.txt 763 | ``` 764 | 765 | 766 | 767 | egrep has an optional `-i` argument, which makes its pattern matching *case-**in**sensitive* 768 | 769 | ```bash 770 | $ egrep -i "Rabbit" alice.txt -> match all "Rabbit" or "rabbit" 771 | 772 | $ egrep -i "CS246" 773 | this will match: cs246 Cs246 CS246 cS246 774 | ``` 775 | 776 | 777 | 778 | - `(c|C)(s|S)246` matches any string that says CS246 with ANY capitalization: Cs246, CS246, cS246, cs246 779 | - square brackets can be used to provide a list of characters and say "Match any one of these characters": `[cC][sS]246` - does same thing as above 780 | - `[cC][sS] ?246`: the question mark means "**0 or 1** of the immediately preceding character (or sub-pattern)", so this matches any capitalization of cs246 that also may have a space between the "cs" and the "246", examples: cs 246, Cs246, cs246, CS 246, ... 781 | - ? makes a pattern *optional* 782 | - The caret `^` with square brackets say "match any character that is NOT one of these characters", use this to look for anything BUT a set of characters. `[^abc]` - match any character that is NOT an a, b, or c. 783 | - The syntax `*` indicates **0 or more** occurrences of the preceding character or sub-pattern. so `(cs)*246` matches 246, cs246, cscs246, cscscs246, ... 784 | - The syntax `+` indicates **1 or more** occurrences of the preceding character or sub-pattern. so `(cs)+246` matches cs246, cscs246, cscscs246, ... 785 | - we could simulate `(cs)+246` by writing `(cs)(cs)*246` 786 | - `[cs]*246` this matches NAY sequence of c's and s's followed by 246: 246, s246, c246, sc246, cs246, sccccc246, cscscscs246, ... 787 | - The character `.` indicates "any single character". `cs.*246` this matches any line that has "cs" then any single character, then 246 788 | - `.*` means match any string 789 | - `.+` means match any NON-EMPTY string 790 | - the special patterns `^` and `$` (when `^` isn't in square brackets), represent the beginning and end of a line, respectively. 791 | - `^cs246` matches only lines that BEGIN with cs246 792 | - `^cs246$` matches only lines that contain exactly cs246 793 | - if we want to match a *special* character, then we need to *escape* that character with an escape character `\` 794 | 795 | ```bash 796 | $ egrep "^Alice.*\.$" alice.txt -> matches any line that begins with the string "Alice", followed by any string, and ends with the period character (being escaped here). 797 | ``` 798 | 799 | - Square brackets will actually automatically escape many of our special characters, because it means "match any of these characters within the square brackets". However, hyphens gain new meaning to create ranges: 800 | - `[a-z]` - match any lowercase character 801 | - `[0-9]` - match any digits 802 | - `[a-zA-Z0-9]` - match any alphanumeric character. Note there are some other ASCII characters between the upper/lowercase rangers, so be careful to use separate ranges `[a-zA-Z]` 803 | - you could use `[.`] to escape the period like `\.` 804 | - If you want to use a hyphen in the square brackets, it goes last 805 | 806 | 807 | 808 | Example: Fetch all lines of even length from alice.txt 809 | 810 | ```bash 811 | $ egrep "^(..)*$" alice.txt 812 | (..)* will only match any line that contains a string of even length, so we need to specify that this is a LINE. 813 | ``` 814 | 815 | Match all odd length lines: 816 | 817 | ```bash 818 | $ egrep "^.(..)*$" alice.txt 819 | OR 820 | $ egrep "^(..)*.$" alice.txt 821 | ``` 822 | 823 | 824 | 825 | Example: list all files in the current directory that contains exactly one "a" 826 | 827 | ```bash 828 | $ ls | egrep "^[^a]*a[^a]*$" 829 | match 0 or more non-a characters, then a, then 0 or more non-a characters 830 | ``` 831 | 832 | 833 | 834 | Examples: fetch all words in the global dictionary that start with e and consists of exactly of five characters 835 | 836 | ```bash 837 | $ egrep "^e....$" /usr/share/dict/words 838 | if we dont want apostrophie 839 | $ egrep "^e[^'][^'][^'][^']$" 840 | ``` 841 | 842 | 843 | 844 | 845 | 846 | 847 | 848 | 849 | 850 | ## 1.4 Bash Scripts 851 | 852 | 853 | 854 | **Shell Script**: File that contains a sequence of shell (bash) commands, executed as programs. 855 | 856 | 857 | 858 | Example: Print the date, current user, and current directory 859 | 860 | ```bash 861 | #!/bin/bash --> shebang line 862 | date 863 | whoami 864 | pwd 865 | ``` 866 | 867 | To execute this bash program, it must have execute bits set, so first change its permission `chmod u+x printStuff` 868 | 869 | Then specify to execute `./` : `./printStuff`, this is giving a relative path, we need to provide a path (either absolute or relative) to the program to tell bash: don't search for this program in the list of programs, just run it. Otherwise, bash looks for it in the directories listed in your "PATH" variable. 870 | 871 | The line `#!/bin/bash` is called the **shebang** line, indicating that the file should be executed as a bash script. This line tells the executing shell which shell language the script is written in. 872 | 873 | Specifically, `#!` says "this should be executed as a script", and `/bin/bash` says "here is the interpreter to use to interpret this script" 874 | 875 | Note: you can change your shebang line to `#!/bin/bash -x`, the `-x` option tells bash to print every command and its arguments as it executes. 876 | 877 | 878 | 879 | 880 | 881 | ### Variables 882 | 883 | Bash has variables 884 | 885 | Naming conventions: (similar to C) 886 | 887 | - start with a letter or underscore 888 | - consists of letters, digits, and underscores 889 | 890 | ```bash 891 | $ x=1 892 | ``` 893 | 894 | NOTE: very important: **no spaces**. 895 | 896 | ```bash 897 | $ echo $x 898 | >1 899 | ``` 900 | 901 | Notes: 902 | 903 | - use `$` when fetching the value of a variables, 904 | - no `$` when setting the variable, (`$` = fetch the value of) 905 | - Good practice: use curly braces when reading a bash variable `$ echo ${x}` 906 | - All variables in bash are *strings*, e.g. `x` above contains the string "1" 907 | 908 | ```bash 909 | dir=~/cs246 --> set dir as a variable 910 | echo ${dir} 911 | > /u9/y287yuan/cs246 912 | ``` 913 | 914 | 915 | 916 | #### Some important global variables 917 | 918 | **PATH**: list of directories, these directories are where the shell will search for commands when you ask it to execute a program. It searches in order that list of directories for a program with that name. 919 | 920 | Note: if there are two files with the same name in two separate directories in PATH, then it will find the execute the one in the first path found. 921 | 922 | ```bash 923 | $ echo "${PATH}" 924 | > will expand the PATH directories (prints the value of the PATH variable) 925 | $ echo '${PATH}' 926 | > ${PATH} (prints literally the string "${PATH}") 927 | ``` 928 | 929 | 930 | 931 | We can use the `env` command to see a number of global variables. 932 | 933 | 934 | 935 | #### Special Variables for Scripts 936 | 937 | **`$0`** - the *first* command line argument, it is the command being executed, i.e. the shell-script name 938 | 939 | **`$1, $2, etc.`** - command line arguments! based on their position 940 | 941 | **`$#` **- the total number of command line arguments 942 | 943 | **`$?`** - the exit/return value of the most-recently executed command, used to tell us whether it succeeded or failed, *0 means success*, while non-0 means failure 944 | 945 | 946 | 947 | If you run `echo $0` when not in a shell script, it is asking "what is the current running program", which is our shell -bash 948 | 949 | 950 | 951 | Example: Check whether a word is in the dictionary 952 | 953 | e.g. ./isItAWord hello 954 | 955 | Have it print nothing if the word is not found, print the word if it is found 956 | 957 | ```bash 958 | #!/bin/bash 959 | egrep "^${1}$" /usr/share/dict/words --> since each line contains one word 960 | 961 | change mode: 962 | chmod u+x isItAWord 963 | 964 | run it: 965 | ./isItAWord 966 | ``` 967 | 968 | 969 | 970 | 971 | 972 | #### Selection Statements 973 | 974 | Example: A good password should not be a word in the dictionary, answer whether a password is *definitely bad* (i.e., in the dictionary), or *might be okay* (i.e. not in the dictionary) 975 | 976 | ```bash 977 | #!/bin/bash 978 | egrep "^${1}$" /usr/share/dic/words > /dev/null 979 | # Note: redirecting to /dev/null supresses output, it goes nowhere 980 | # Note: every program produces a status code when it is done executing 981 | # Exit status for egrep: 0 if a line is selected, 1 if no lines were selected, 2 if error occured 982 | # $? = retrieve the status code of the most recently executed program 983 | 984 | if [ ${?} -eq 0 ]; then # by using -eq we can force bash variables to be treated as numbers (by default they are strings) 985 | echo "Bad password" 986 | else 987 | echo "Maybe not a terrible password" 988 | fi # end your if in bash 989 | ``` 990 | 991 | **Note**: `/dev/null` is the Linux equivalent of throwing the standard output into the garbage 992 | 993 | 994 | 995 | If the user executes our program with the wrong number of arguments, we should let them know: 996 | 997 | ``` bash 998 | #!/bin/bash 999 | 1000 | usage() { 1001 | echo "Usage: $0 password" # $0 is the name of program being executed 1002 | } 1003 | # $# = retrieve the number of arguments 1004 | if [ ${#} -ne 1]; then 1005 | usage # usage tells the user how to use this script 1006 | exit 1 # terminate our script with a failure (non-zero) value 1007 | fi 1008 | 1009 | egrep "^${1}$" /usr/share/dict/words > /dev/null 1010 | 1011 | if [ $? -eq 0]; then 1012 | echo "Bad password" 1013 | else 1014 | echo "Maybe not a terrible password" 1015 | fi 1016 | ``` 1017 | 1018 | **Note**: by default, the program returns 0 to indicate success if it runs to completion. 1019 | 1020 | `>&2` redirects standard input to standard error 1021 | 1022 | 1023 | 1024 | General form of an `if` in bash 1025 | 1026 | ```bash 1027 | if ; then # use semicolon to separate "then" 1028 | 1029 | elif ; then 1030 | 1031 | elif ; then 1032 | ... 1033 | else 1034 | 1035 | fi 1036 | ``` 1037 | 1038 | Be careful with spacing **!** There must be a blank space between the opening square bracket and the condition being tested, and a blank space between the condition and the closing square bracket. 1039 | 1040 | ```bash 1041 | if [ ${foo} = "cat" ]; then 1042 | ... 1043 | fi 1044 | 1045 | if [ $foo -eq 2 ]; then 1046 | ... 1047 | fi 1048 | ``` 1049 | 1050 | - Use `-a` for "and" and `-o` for "or", see `man [` 1051 | 1052 | 1053 | 1054 | #### Loops in Bash 1055 | 1056 | There are two types of loops in bash: a *counted* loop, and a loop that *iterates over a list* of items 1057 | 1058 | **Counted loop** 1059 | 1060 | A counted loop requires a variable to which we add or subtract a value, or modify in some way. The loop terminates when we reach a specific value or some condition evaluates to true. 1061 | 1062 | ```bash 1063 | while [ cond ]; do 1064 | ... 1065 | done 1066 | ``` 1067 | 1068 | Example -- printNumbers 1069 | 1070 | ```bash 1071 | x=1 1072 | while [ ${x} -le ${1}]; do 1073 | echo ${x} 1074 | # we need to increment x, but x is a string 1075 | # we need to force arthimetic 1076 | x=$((x+1)) # $((...)) is the syntax for arithmetic operations 1077 | done 1078 | ``` 1079 | 1080 | **Note**: the special syntax `$(( ... ))` is used to tell the shell that the information within the doubled-parentheses are to be **treated as integers**. (by default bash treats values as strings) 1081 | 1082 | 1083 | 1084 | Some options if you want to print the numbers on the same line: 1085 | 1086 | ```bash 1087 | printNumbers 1088 | 1089 | #!/bin/bash 1090 | x=1 1091 | out="" # introduce an empty string "out" 1092 | while [ ${x} -le ${1}]; do 1093 | out="$out $x" # concatenate the string 1094 | x=$((x+1)) 1095 | done 1096 | echo ${out} 1097 | ``` 1098 | 1099 | another way to use `echo -n` which does not output the trailing new lines 1100 | 1101 | ```bash 1102 | printNumbers 1103 | #!/bin/bash 1104 | x=1 1105 | out="" # introduce an empty string "out" 1106 | while [ ${x} -le ${1}]; do 1107 | echo -n $x " " # we can add a space between each number 1108 | x=$((x+1)) 1109 | done 1110 | echo "" # we can add a new line at the end 1111 | ``` 1112 | 1113 | 1114 | 1115 | **List iteration Loop** 1116 | 1117 | Looping over a list of (string) values, either written directly into the loop, or generated as the result of executing a command. 1118 | 1119 | ```bash 1120 | for variable in list; do 1121 | command1 1122 | command2 1123 | ... 1124 | done 1125 | ``` 1126 | 1127 | 1128 | 1129 | listLoop: 1130 | 1131 | ```bash 1132 | #!/bin/bash 1133 | for x in a b c; do echo $x; done # looping over the iterable things (a b c) we put here 1134 | 1135 | >> a 1136 | >> b 1137 | >> c 1138 | ``` 1139 | 1140 | 1141 | 1142 | Example: How many times does the word $1 occur in the file given in \$2 1143 | 1144 | ```bash 1145 | countWords 1146 | 1147 | #!/bin/bash 1148 | 1149 | x=0 1150 | for word in $(cat $2); do 1151 | if [ $word = $1 ]; then # equal = is for string equality 1152 | x=$((x+1)) 1153 | fi 1154 | done 1155 | echo $x 1156 | ``` 1157 | 1158 | 1159 | 1160 | #### Functions 1161 | 1162 | Functions can only return a positive integer value in the range 0 to 255. 1163 | 1164 | We specify the contents of the function's body by putting statements inside curly braces `{}` 1165 | 1166 | 1167 | 1168 | Example: Payday is the last Friday of each month. Compute this month's payday. (2 tasks: compute the date; present the answer) 1169 | 1170 | ```bash 1171 | payday 1172 | 1173 | #!/bin/bash 1174 | 1175 | # present the answer 1176 | report() { # Inside a function $1, etc denote the "functions" parameters 1177 | if [ $1 -eq 31 ]; then 1178 | echo "This month: the 31st" 1179 | else 1180 | echo "This month: the ${1}th" 1181 | fi 1182 | } 1183 | 1184 | # figure out the date 1185 | report $(cal | awk '{print $6}' | egrep "[0-9]" | tail -1) 1186 | 1187 | ``` 1188 | 1189 | 1190 | 1191 | Example: Generalize this script to any month (not just the current) 1192 | 1193 | ```bash 1194 | payday 1195 | 1196 | #!/bin/bash 1197 | 1198 | report() { 1199 | if [ $1 -eq 31 ]; then 1200 | echo "$2 $3: the 31st" 1201 | elif [ $1 -eq 23 ]; then 1202 | echo "$2 $3: the 23rd" 1203 | elif [ $1 -eq 22 ]; then 1204 | echo "$2 $3: the 22nd" 1205 | else 1206 | echo "$2 $3: the ${1}th" 1207 | fi 1208 | } 1209 | 1210 | # figure out the date 1211 | report $(cal $1 $2 | awk '{print $6}' | egrep "[0-9]" | tail -1) $1 $2 1212 | 1213 | ``` 1214 | 1215 | 1216 | 1217 | Example: 1218 | 1219 | ```bash 1220 | #!/bin/bash 1221 | 1222 | foo() { 1223 | echo "name is: " ${0} 1224 | echo "foo argument 1 is: " ${1} 1225 | echo "foo argument 2 is: " ${2} 1226 | echo "foo argument 3 is: " ${3} 1227 | if [ ${1} = "cat" ]; then 1228 | return 0 1229 | fi 1230 | return 1 1231 | } 1232 | 1233 | # calls foo 1234 | foo ${3} ${2} ${1} 1235 | echo 'foo returned ${?}' ${?} # print the status code 1236 | ``` 1237 | 1238 | 1239 | 1240 | 1241 | 1242 | 1243 | 1244 | 1245 | 1246 | 1247 | 1248 | 1249 | 1250 | -------------------------------------------------------------------------------- /11. Vtables and Multiple Inheritance.md: -------------------------------------------------------------------------------- 1 | # 11. Vtables and Multiple Inheritance 2 | 3 | 4 | 5 | ## How Virtual Methods Work? (March 31) 6 | 7 | How exactly is the correct `virtual` method chosen dynamically at run-time? 8 | 9 | ```cpp 10 | struct A { virtual void hello() { cout << "I'm an A"; } }; 11 | 12 | struct B : public A { void hello() override { cout << "I'm a B"; } }; 13 | 14 | int main() { 15 | int x; 16 | cin >> x; 17 | A *ap; 18 | if (x < 0) ap = new A; 19 | else ap = new B; 20 | } 21 | 22 | // By what magic does the compiler know what to do for ap->hello() ? 23 | ``` 24 | 25 | 26 | 27 | When `ap->hello()` is called, then compiler observes this is a virtual method. Instead of generating code to jump to `A::hello()` based on the fact that `ap` is an A pointer, the compiler instead does **virtual dispatch**. 28 | 29 | How does it achieve virtual dispatch? 30 | 31 | - How is not dictated by the C++ standard 32 | - What virtual must be is 33 | 34 | But, most compilers will do the following: 35 | 36 | Consider, how an object is laid out in memory: 37 | 38 | ```cpp 39 | class Vec { 40 | int x, y; 41 | public: 42 | int doSomething() {...} 43 | }; 44 | 45 | class Vec2 { 46 | int x, y; 47 | public: 48 | virtual int doSomething() {...} 49 | }; 50 | 51 | // do they look the same in memory? 52 | Vec v{1, 2}; 53 | Vec2 w{1, 2}; 54 | 55 | // no, they have different sizes: 56 | cout << sizeof(v) << " " << sizeof(w) << endl; 57 | // prints 8 and 16, why? 58 | ``` 59 | 60 | Note: 8 is space for ints for no space allocated for doSomething() method (in Vec). 61 | 62 | 63 | 64 | So how does the operating system know where to find the `doSomething()` function? 65 | 66 | Well, it has an address in memory, and any call to the function is replaced by a transfer to that memory location. 67 | 68 | The compiler turns methods into ordinary functions that stores them separately from objects. 69 | 70 | 71 | 72 | ## VTable (April 5) 73 | 74 | Recall the book example: If isHeavy() is virtual, choice of which version to run is based on the type of the actual object - which the compiler can't know in advance. 75 | 76 | How is the correct version of isHeavy() been chosen at runtime by the compiler? 77 | 78 | For each class with virtual methods, the compiler creates a *table of function pointers* (the **vtable**), and instances of that class have an extra pointer (the vptr or virtual function table ptr) that point to the class's vtable. 79 | 80 | Every instance of the class now contains a virtual pointer to the vtable. 81 | 82 | 83 | 84 | ![11-1](https://github.com/kila097/CS246_Notes/blob/main/images/11-1.png) 85 | 86 | Example: 87 | 88 | ```cpp 89 | class C { 90 | int x, y; 91 | virtual void f(); 92 | virtual void g(); 93 | void h(); 94 | virtual ~C(); 95 | } 96 | ``` 97 | 98 | Each of the objects c and d, have a field for the vptr, and the two data fields x and y. Each vptr points to the vtable (virtual table) for the class. The vtable contains an entry for each virtual method, that points to the location of the method code in memory. 99 | 100 | 101 | 102 | Revisit our Book and Text example: 103 | 104 | ![11-3](https://github.com/kila097/CS246_Notes/blob/main/images/11-3.png) 105 | 106 | Each object has the data fields appropriate to its type (a Book has title, author, and numPages, while a Text has title, author, numPages, and topic), and the **vptr**. The vptr for a Book points to the Book vtable, while the vptr of the Text points to the Text vtable. Each of the vtables has an entry for the virtual isHeavy method, and they point to the different locations where each class implementation is stored in memory. 107 | 108 | Consider: 109 | 110 | ```cpp 111 | Book *bptr = new Text{...}; 112 | ... 113 | cout << bptr->isHeavy() << endl; 114 | ``` 115 | 116 | In order to call the correct virtual method for whatever bptr is pointing to, we need to: 117 | 118 | 1. Follow the object's vptr to the vtable 119 | 2. Fetch the address for the isHeavy method for that class 120 | 3. Go to the address for that isHeavy method 121 | 4. Execute the method code at that address 122 | 123 | 124 | 125 | 126 | 127 | Calling a ***non-virtual*** method: 128 | 129 | - compiler makes space on the stack for locals/parameters 130 | - jumps to the location in code where that function exists 131 | 132 | Calling a ***virtual*** method on a base class ptr/reference: 133 | 134 | - follows the vptr to the vtable 135 | - fetches ptr to the function from the location in code 136 | - makes room on stack and jumps to location in code as specified by that function pointer (calls the function) 137 | 138 | This happens at runtime. Therefore, virtual function calls incur a small overhead cost (small performance penalty). 139 | 140 | Also, declaring at least one virtual method adds a vptr to the object, therefore classes without virtual methods produces smaller objects than if some functions were virtual. 141 | 142 | 143 | 144 | How is the information actually laid out in memory? Well, that depends upon the compiler implementation. 145 | 146 | Consider: 147 | 148 | ```cpp 149 | class A { 150 | public: 151 | int a, c; 152 | virtual void f(); 153 | } 154 | 155 | class B : public A { 156 | public: 157 | int b, d; 158 | } 159 | ``` 160 | 161 | In g++, the vptr is the first data field in the object, then the data fields `a` and `c` follow, in that order, for a type A object. For a type B object, which inherits from A, the vptr is followed by A's data fields, then B's data fields. 162 | 163 | Structurally, it looks like this: 164 | 165 | ![11-4](https://github.com/kila097/CS246_Notes/blob/main/images/11-4.png) 166 | 167 | The vptr pointers are 8-byte long in C++, they take up 2 integers worth of information. 168 | 169 | 170 | 171 | 172 | 173 | ## Multiple Inheritance (April 5) 174 | 175 | A class can inherit from more than one class. (In java, you can only inherit from one class) 176 | 177 | 178 | 179 | ```cpp 180 | struct A { 181 | int a; 182 | }; 183 | 184 | struct B { 185 | int b; 186 | }; 187 | 188 | class C : public A, public B { 189 | void f() { 190 | cout << a << " " << b << endl; 191 | } 192 | }; 193 | ``` 194 | 195 | 196 | 197 | 198 | 199 | 200 | 201 | Suppose B and C both inherit from A, suppose also that class D inherit from B and C. 202 | 203 | ![11-2](https://github.com/kila097/CS246_Notes/blob/main/images/11-2.png) 204 | 205 | (the deadly diamond, or the diamond of death, or the deadly diamond of death) due to the shape of the inheritance hierarchy 206 | 207 | Further suppose A has field `int a;`. Then consider: 208 | 209 | ```cpp 210 | D dobj; 211 | dobj.a; // which a is this? B's a or C's a? 212 | ``` 213 | 214 | The access to this field is ambiguous! D has two `a` fields, one from B and one from C. We need to specify by using the scope resolution operator `::` to pick the version we want to access: 215 | 216 | ```cpp 217 | dobj.B::a; 218 | // or 219 | dobj.C::a; 220 | ``` 221 | 222 | However, we have two copies of the data fields for class A since the default behaviour of the compiler is to treat them as being two different things. 223 | 224 | But, if this is coming from class A, do we really want two copies? 225 | 226 | **The solution**: make class A a **virtual base class** and employ **virtual inheritance** to specify classes B and C. This tells the compiler that there will be only one instance of the superclass object in memory for the concrete subobject. 227 | 228 | ```cpp 229 | class B : virtual public A {...}; 230 | class C : virtual public C {...}; 231 | ``` 232 | 233 | If we do that, D will have only one copy of `a`, great! 234 | 235 | ```cpp 236 | A *a = new D; // this points to a D 237 | // we have to do dynamic casting, where does the A field go? 238 | ``` 239 | 240 | 241 | 242 | The memory layout for object type D roughly looks like: 243 | 244 | ![11-6](https://github.com/kila097/CS246_Notes/blob/main/images/11-6.png) 245 | 246 | 247 | 248 | 249 | 250 | The real life example in C++ I/O library, `basic_ostream` virtually inherits from `basic_ios`, and `ostream` inherits from `basic_ostream`. 251 | 252 | ![11-5](https://github.com/kila097/CS246_Notes/blob/main/images/11-5.png) 253 | -------------------------------------------------------------------------------- /2. Software Testing.md: -------------------------------------------------------------------------------- 1 | # 2. Software Testing 2 | 3 | **Software Testing** is an investigation conducted to verify if the software works as intended, identifying existing errors, and possibly assess the quality of the software. 4 | 5 | ## Testing 6 | 7 | - Testing is an essential part of program development 8 | - testing is ongoing, shouldn't just do it at the end 9 | - begins *before* you start coding 10 | - continues while you start coding 11 | - test suites - test the expected behaviour of program 12 | 13 | - Testing is NOT debugging, you can't debug without testing. Debugging is the process that identifies the causes of known errors to solve them. Testing occurs both before and after debugging 14 | - Testing cannot guarantee correctness, but it can prove wrongness 15 | - Testing is hard 16 | - no general formula 17 | - psychological barrier - don't want to find out that your program is wrong 18 | - Ideally, developer and tester should be different people 19 | 20 | 21 | 22 | **Human testing**: 23 | 24 | - humans look over the code and find flaws (reading through your code) 25 | - code inspection, walkthrough (explaining what happens step by step) 26 | 27 | 28 | 29 | **(Automated) Machine testing**: 30 | 31 | - run program on selected input, check against specification 32 | - can't check everything, not possible to test every possible inputs (it's infinite sometimes), choose test cases carefully and effectively 33 | - black/white/grey box testing - no/full/some knowledge of program implementation 34 | 35 | 36 | 37 | 38 | 39 | ## Types of Software Tests 40 | 41 | **Unit tests** are conducted at the lowest level, testing only one specific module/unit of the software. The goal of unit tests is to verify if the unit of code works as intended and identify any existing errors (bugs). 42 | 43 | 44 | 45 | **Integration Tests** are conducted at a level above the unit tests and aim to verify if the different modules/units of the software work correctly together. These tests should perform operations that involve multiple modules or classes and check if they work as intended. 46 | 47 | 48 | 49 | **Functional Tests (or System Tests)** focus on the business requirements of the application. They can be conducted by executing the application with a specific set of inputs and verifying if the application produces the correct outputs. We are not concerned with how the results are being produced (which should have already been tested in the unit and integration tests), only if they are correct. 50 | 51 | 52 | 53 | **Acceptance Tests** are usually part of a formal process in which the client must verify that the produced software meets all the requirements, so it can be accepted. Client acceptance is generally a condition to move to the next phase of a project. 54 | 55 | Sometimes, acceptance tests may be detailed into phases such as **alpha testing** (done at the end of the development, by a subset of users, but in the controlled environment of the developer) and **beta testing** (done at the end of the development after the alpha tests, by a subset of real users, in the user's environment). 56 | 57 | 58 | 59 | **Regression Tests** are conducted after any modification in the software, to ensure that it continues working as intended, i.e., that the modification did not introduce new errors. Ideally, regression tests should be automated, so that they can quickly be executed after each code modification. One possible way to conduct regression tests is to create automated unit, integration, and functional tests, so they can be executed again after each modification. 60 | 61 | - Make sure new changes to a program don't break old test cases 62 | - test suites, testing scripts 63 | - always add; never subtract 64 | 65 | 66 | 67 | **Performance Tests** are designed to verify if the run-time performance of the system will be adequate. Some systems may work well when tested by the developer in a single-user environment but may have performance issues when tested, for example, with multiple concurrent users or with large data sets. 68 | 69 | - is this program efficient enough? 70 | 71 | 72 | 73 | **Accessibility, Usability, and User Experience Tests** The goal of these tests is to verify the quality of the experience of the user interacting with the system. Software may do what it is supposed to do (i.e., it passes all the tests above), but it may be for example hard to use or learn, or may not support users with disabilities. 74 | 75 | 76 | 77 | ## White/Black Box Tests 78 | 79 | Start with black box testing, and supplement with white box testing. 80 | 81 | **Black box** testing: 82 | 83 | - tests are created based only on the requirement specifications, without any knowledge of the internal structure of the program (i.e., without access to the source code). 84 | - commonly used for functional and acceptance tests 85 | 86 | **White box** testing: 87 | 88 | - tests are created with knowledge of the internal structure of a program. 89 | - generally used to verify the internal structures or workings of a program 90 | 91 | - unit tests commonly used white-box testing to verify that each function or method is working correctly 92 | 93 | 94 | 95 | **Grey box** testing: 96 | 97 | - a mix of white and black box testing. 98 | 99 | - test focusing on the expected outcomes, having knowledge of the code 100 | 101 | 102 | 103 | DO NOT TEST - invalid input, unless behaviour for invalid has been prescribed 104 | 105 | - if the input is invalid (and the behaviour for that is unspecified) then the output is unspecified meaning there is no correct input. Any such test case is meaningless. 106 | 107 | 108 | 109 | #### Creating black-box tests 110 | 111 | Check the specification and try to identify all the different types of inputs that the program should accept. 112 | 113 | Then, identify what should be the correct output to each set of inputs according to the specification. 114 | 115 | Useful Guidelines: 116 | 117 | - Various classes of inputs, e.g. numeric ranges, positive numbers vs. negative numbers 118 | - don't need to test invalid input (as is not in the specification) 119 | - boundaries of valid ranges (edge cases), e.g. minimum and maximum values 120 | - multiple simultaneous boundaries (corner cases) (two edge cases in same test case) 121 | - Various classes of inputs, e.g. numeric ranges, positive numbers vs. negative numbers 122 | - don't need to test invalid input 123 | - boundaries of valid ranges (edge cases) 124 | - multiple simultaneous boundaries (corner cases) (two edge cases in same test case) 125 | - many good testing comes from intuition/experience - guess at likely error 126 | - extreme cases (within reason) (e.g. very large numbers) 127 | - many good testing comes from intuition/experience - guess at likely errors 128 | 129 | 130 | 131 | #### Creating white-box tests 132 | 133 | Examine the code and create enough test cases to ensure that all parts work as intended 134 | 135 | - execute all logical paths through the program 136 | - make sure every function runs 137 | 138 | 139 | 140 | -------------------------------------------------------------------------------- /3. Basic C++.md: -------------------------------------------------------------------------------- 1 | # C++ 2 | 3 | 4 | 5 | Hello World in C: 6 | 7 | ```c 8 | #include 9 | int main() { 10 | prinf("Hello world\n"); 11 | return 0; 12 | } 13 | ``` 14 | 15 | 16 | 17 | Hello World in C++: 18 | 19 | ```cpp 20 | #include 21 | using namespace std; 22 | 23 | int main() { 24 | cout << "Hello world" << endl; 25 | return 0; 26 | } 27 | ``` 28 | 29 | `endl` prints out a newline and flush the buffer. 30 | 31 | Notes: 32 | 33 | - main *must* return int in c++ 34 | - stdio.h and printf are still available in C++, but we are not going to use them 35 | - preferred C++ I/O header: 36 | - output: `sts::cout << ... << ... << ... ;` 37 | - `std::endl` = end of line 38 | - `using namespace std;;` allows us to omit the `std::` prefix 39 | - return statement; in main returns the status code to the shell ($?); can be omitted from main (0 is assumed) 40 | 41 | 42 | 43 | ## Compiling 44 | 45 | Compile C++ programs: 46 | 47 | ```` 48 | g++ -std=c++14 program.cc -o program 49 | ```` 50 | 51 | If the `-o program` (name of the program) is not specified, the compiler will produce a program named a.out 52 | 53 | 54 | 55 | *Most* C programs are valid C++ programs. 56 | 57 | Other compiling flags: 58 | | flag | use| 59 | | ------------ | ------------------------------------------------------------ | 60 | | `-g` | Produce debugging information in the operating system's native format. The GNU debugger, `gdb`, can work with this debugging information. Makes your code larger, but useful while trying to get everything working. | 61 | | `-c` | Produce an object file (ends in .o) that consists of assembler output. Will be useful once we discuss *separate compilation*. | 62 | | `-Wall` | This enables all the warnings about constructions that some users consider questionable, and that are easy to avoid (or modify to prevent the warning). Not necessary, but a good idea. | 63 | | `-Wextra` | This enables some extra warnings that aren't enabled by `-Wall`. Not necessary, but a good idea. | 64 | | `-Wpedantic` | This ensures that your code follows the strict ISO C++ standard, with no forbidden extensions. Not necessary, but a good idea. | 65 | | `-D` | Lets us define a macro name as a command-line argument to the compiler. Useful for selectively adding/removing code during the compilation process. | 66 | 67 | 68 | 69 | 70 | 71 | ## Input/Output 72 | 73 | There are 3 I/O streams: 74 | 75 | - stdout/stderr connected to std::cout/std::cerr 76 | - stdin connected to std::cin 77 | 78 | 79 | 80 | ### I/O operators 81 | 82 | - `<<` = "put to" (output), e.g. `cerr << x` "put x to cerr" 83 | - `>>` = "get from" (input), e.g. `cin >> x` "cin read into x" 84 | - very easy to remember which operator is which because the arrows are pointing in the "flow" of the information 85 | 86 | 87 | 88 | Example: addTwoNumbers 89 | 90 | ```cpp 91 | #include 92 | using namespace std; 93 | 94 | int main() { 95 | int x,y; 96 | cin >> x >> y; // cin >> x; cin >> y; 97 | cout << x + y << endl; 98 | } 99 | ``` 100 | 101 | Notes: 102 | 103 | - `cin >>` ignores whitespace (space/tab/newline) 104 | - `cin >> x >> y` gets two integers from stdin, skipping whitespace 105 | 106 | 107 | 108 | What if the input doesn't contain an integer next? Or the integer is too larger/too small? What is the input is exhausted before we get two integers? Statement fails; value of variable is one 0, max-int, or min-int. 109 | 110 | 111 | 112 | If the read fails: `cin.fail()` will be true (We can check the fail bit of cin) 113 | 114 | If we have reached EOF: `cin.eof() cin.fail()` will be true, BUT not until the attempted read fails. Only if you *try* to read something, and found that it is EOF 115 | 116 | 117 | 118 | Example: Read all integers from stdin, and echo them out, one per line, to stdout. Stop on any bad input or EOF 119 | 120 | ```cpp 121 | #include 122 | using namespace std; 123 | 124 | int main() { 125 | int i; 126 | while (true) { 127 | cin >> i; 128 | if (cin.fail()) break; 129 | cout << i << endl; // print out one per line 130 | } 131 | } 132 | ``` 133 | 134 | 135 | 136 | Version 2.0 137 | 138 | ```cpp 139 | #include 140 | using namespace std; 141 | // There is an implicit conversion from cin to bool. cin is regarded as true if its fail (or bad) bit are not set. This permits cin to be used as a condition. 142 | 143 | int main() { 144 | int i; 145 | while (true) { 146 | cin >> i; 147 | if (!cin) break; 148 | cout << i << endl; // print out one per line 149 | } 150 | } 151 | ``` 152 | 153 | 154 | 155 | Version 3.0 156 | 157 | Recall: cin >> x >> y; // equivalent to cin >> x; cin >> y; 158 | 159 | We get (cin >> x) >> y; 160 | 161 | The input operator >>, has cin (of type istream) as its first operand and some data (in this case an int, but it could be of several possible types) as its second operand. 162 | 163 | What is the output (Return value) of this operator? 164 | 165 | :arrow_right: It returns back the stream itself (in this case: cin, cin is the return value) 166 | 167 | If a read returns back cin, and cin can be used as a bool, we can simplify this further: 168 | 169 | Version3.0 170 | 171 | ```cpp 172 | #include 173 | using namespace std; 174 | 175 | int main() { 176 | int i; 177 | while (cin >> i) { // if we read into i and it is successful 178 | cout << i << endl; // print out one per line 179 | } 180 | } 181 | ``` 182 | 183 | 184 | 185 | Example: Read ALL ints and echo to stdout until EOF. Skip non-integer input. 186 | 187 | ```cpp 188 | #include 189 | using namespace std; 190 | 191 | int main() { 192 | int i; 193 | while (true) { 194 | if (!(cin >> i)) { // either non-int input, or readed EOF 195 | if (cin.eof()) break; // if we've reached EOF we're done 196 | // if we haven't reached EOF we have a failed read, and the 197 | // fail bit is set. If the fail bit is set, no reads from 198 | // the stream can occur, 199 | // so we need to turn the fail bit off. 200 | cin.clear(); // clears the fail bit 201 | // If a read fails, nothing is removed from the input stream, so the 202 | // offending character that stopped us from reading an integer 203 | // is still there. So we need to remove it. 204 | cin.ignore(); // Ignores the current input character 205 | 206 | } 207 | cout << i << endl; 208 | } 209 | } 210 | ``` 211 | 212 | 213 | 214 | Note: 215 | 216 | cout << 95 << endl; // prints 95 217 | 218 | What if we wanted to print the number in hexadecimal? 219 | 220 | cout << hex << 95 << endl; // print 5f (in hexadecimals) 221 | 222 | `std::hex` is an I/O manipulator. It causes all subsequent integers to be printed in hexadecimal. 223 | 224 | To switch back, use `cout << std::dec`. For all other manipulators see **** manpage online. 225 | 226 | Potentially useful: `std::noskipws` (Note, only works when reading chars) 227 | 228 | 229 | 230 | ## Strings 231 | 232 | In C: a string is an array of characters (char * or char[]), terminated by \0 (the null character) 233 | 234 | - Must explicitly manage your memory, allocate more memory as strings get larger 235 | - Easy to accidently overwrite \0 and corrupt memory 236 | 237 | 238 | 239 | C++ strings: `#include , type std::string` 240 | 241 | - grows as needed (no need to manage memory) 242 | - safer to manipulate (a lot harder to mess up invariants) 243 | 244 | 245 | 246 | ```cpp 247 | #include 248 | std::string s = "Hello"; 249 | std::string name{ "Jane" }; 250 | ``` 251 | 252 | Even in C++, the literal "Hello" represents a C-style string (a C constant character pointer), i.e. a null-terminated array of characters. 253 | 254 | The variable s is a C++ string whose contents are initialized based on the C-string "Hello" 255 | 256 | 257 | 258 | Some string operations: 259 | 260 | - equality and inequality: s1 == s2, s1 != s2 261 | - comparison: s1 <= s2, (lexicographic order) 262 | - length: s1.length(), s1.size() 263 | - fetch individual chars: s[0], s[1], s[2], etc. (indexing starts at 0 and goes up to s.length()-1) 264 | - concatenation: s3 = s1 + s2; s3 += s4; 265 | - check out [manpages](http://www.cplusplus.com/reference/string/string/) for string 266 | 267 | 268 | 269 | ```cpp 270 | #include 271 | using namespace std; 272 | 273 | int main() { 274 | string s; 275 | while (cin >> s) { 276 | // Reads a string (skipping leading whitespace) and stops reading at next ws 277 | // (i.e. read one word) 278 | cout << s; 279 | } 280 | } 281 | ``` 282 | 283 | 284 | 285 | If we want to read an entire line (including whitespace), we can use getline 286 | 287 | `getline(cin, s)`reads until the next newline character into s. 288 | 289 | ```cpp 290 | #include 291 | #include 292 | #include 293 | using namespace std; 294 | 295 | int main() { 296 | int i = 1; 297 | string s; 298 | while (getline(cin,s)) { // getline doesn't skip initial whitespaces 299 | cout << setw(3) << i++ << ":\"" << s << '\"' << endl; 300 | } 301 | } 302 | ``` 303 | 304 | `getline()` returns false when failure or end-of-file. 305 | 306 | 307 | 308 | We can also read characters 309 | 310 | ```cpp 311 | #include 312 | using namespace std; 313 | 314 | int main() { 315 | char c; 316 | while (cin >> noskipws >> c) { 317 | cout << c; 318 | } 319 | } 320 | ``` 321 | 322 | Change our program to remove subsequent whitespaces: 323 | 324 | ```cpp 325 | #include 326 | using namespace std; 327 | 328 | int main() { 329 | char c; 330 | char lastChar = 'a'; 331 | while (cin >> noskipws >> c) { 332 | if (lastChar == ' ' && c == ' ') continue; // don't print it out 333 | cout << c; 334 | lastChar = c; 335 | } 336 | } 337 | ``` 338 | 339 | 340 | 341 | Lets write our own readNumbers: 342 | 343 | ```cpp 344 | #include 345 | using namespace std; 346 | 347 | int readInt() { 348 | char c; 349 | int sum = 0; 350 | while (true) { 351 | if (cin.peek() < '0' || cin.peek() > '9') breaks; 352 | cin >> c; 353 | // turn it into a number 354 | int x = c - '0'; 355 | sum *= 10; // shift one decimal left (mult by base 10) 356 | sum += x; 357 | } 358 | } 359 | 360 | int main() { 361 | cout << readInt() << endl; 362 | } 363 | ``` 364 | 365 | 366 | 367 | This abstract idea of a stream applies to other sources of data, not just I/O 368 | 369 | ### File I/O 370 | 371 | Example: files, read from a file instead of stdin. Use std::ifstream to read from a file and std::ofstream to write to a file 372 | 373 | Reading from a file is almost identical to reading from stdin, instead of including the `iostream` library, you need to include the `fstream` library and use the `ifstream` type. Writing to a file uses `ofstream` type. 374 | 375 | **File access in C** 376 | 377 | ```c 378 | #include 379 | 380 | int main() { 381 | char s[256]; 382 | FILE *file = fopen("suite.txt", "r"); 383 | while (true) { 384 | fscanf(file, "%255s", s); // read the next word up to 255 characters long; huge headache if word is larger than 255 385 | if (feof(file)) break; 386 | printf("%s\n", s); 387 | } 388 | fclose(file); 389 | } 390 | ``` 391 | 392 | 393 | 394 | **In C++** 395 | 396 | ```cpp 397 | #include 398 | #include 399 | using namespace std; 400 | 401 | int main() { 402 | ifstream file{"suite.txt"}; 403 | // Declaring and initializing an ifstream opens the file 404 | string s; 405 | while (file >> s) { 406 | cout << s << endl; 407 | } 408 | } // file is coused automatically when the ifstream variable goes out of scope 409 | ``` 410 | 411 | 412 | 413 | Anything we can do with cin/cout, we can do with an ifstream/ofstream 414 | 415 | ```cpp 416 | fileIO.cc 417 | 418 | #include 419 | 420 | int main() { 421 | std::ifstream infile{ "input.txt" }; 422 | std::ofstream outfile{ "output.txt" }; 423 | int i; 424 | while ( true ) { 425 | infile >> i; 426 | if ( infile.fail() ) break; 427 | outfile << i << std::endl; 428 | } 429 | } 430 | ``` 431 | 432 | The curly braces `{}` are used to initialize the file stream objects. 433 | 434 | 435 | 436 | ### Command-line Arguments 437 | 438 | The `main` function may take two arguments, `argc` and `argv`, with `argc` representing the number of arguments the program received, and `argv` representing the array of arguments ( is an array of char pointers `char **`) 439 | 440 | ```cpp 441 | args.cc 442 | 443 | #include 444 | 445 | int main(int argc, char **argv) { 446 | for (int argi = 0; argi < argc; argi++) { 447 | std::cout << argv[argi] << std::endl; 448 | } 449 | } 450 | ``` 451 | 452 | This in shell: 453 | 454 | ```bash 455 | $ g++ args.cc -o args 456 | $ ./args This is a test 457 | ./args 458 | This 459 | is 460 | a 461 | test 462 | $ ./args "This is a test" 463 | ./args 464 | This is a test 465 | ``` 466 | 467 | Note: `argv[0]` is the name of the program itself, and normal arguments continue from there. 468 | 469 | 470 | 471 | ### String Streams 472 | 473 | There is a type in C++ called `stringstream`, available in the `#include ` library. It is a hybrid of both the `std::string` class, and the I/O stream classes. It lets you read/write to/from strings using stream operators. Use the type explicitly defined for input: `istringstream`, and the type for output: `ostringstream` 474 | 475 | 476 | 477 | Example: Strings. Use stream to read from /write to strings. 478 | 479 | `#include `, `std::istringstream` to read from a string, and `std::ostringstream` to write to a string. 480 | 481 | ```cpp 482 | sting intToString(int n) { 483 | ostringstream oss; 484 | oss << n; // write a number to a stream 485 | return oss.str(); 486 | } 487 | ``` 488 | 489 | 490 | 491 | E.g. convert a string into a number 492 | 493 | ```cpp 494 | int n; 495 | while (true) { 496 | cout << "Enter a number" << endl; 497 | string s; 498 | cin >> s; 499 | istringstream iss{s}; 500 | if (iss >> n) break; // stop we got a number 501 | cout << s << "is not a number" << endl; 502 | } 503 | cout << "you entered" << n << endl; 504 | ``` 505 | 506 | isstream for "reading from", and osstream for "writing to" 507 | 508 | 509 | 510 | **Revisit**: Echo all integers, skip non-integers 511 | 512 | ```cpp 513 | #include 514 | #include 515 | using namespace std; 516 | // Read all integers, skip non-integers 517 | int main() { 518 | string s; 519 | while (cin >> s) { 520 | istringstream iss{s}; 521 | int n; 522 | if (iss >> n) cout << n << endl; 523 | } 524 | } 525 | ``` 526 | 527 | 528 | 529 | **Input string streams** 530 | 531 | The primary purpose for using an input string stream is to take an existing string, such as the sentence "The quick brown fox\njumped over the lazy\tdog.", and split it up into separate words. By default, the `istringstream` separates the words (often called tokens) by whitespace. (Remember, *whitespace* consists of blanks, tabs, and newlines). 532 | 533 | The input stream function `std::getline` lets us specify the *delimiter* to use when reading in lines of input as strings, we can specify a delimiter to separate our tokens. 534 | 535 | Example: istringstream.cc 536 | 537 | ```cpp 538 | #include 539 | #include 540 | #include 541 | using namespace std; 542 | 543 | int main() { 544 | string s; 545 | string s1{ "The quick brown fox\njumped over the lazy\t dog." }; 546 | istringstream ss1{s1}; 547 | while (ss1 >> s) { 548 | cout << s << endl; 549 | } 550 | string s2{"Smith, Jane, 99999, Yu, Hello, 88888"}; 551 | istringstream ss2{s2}; 552 | cout << "***" << endl; 553 | while (getline(ss2, s, ',')) { 554 | cout << s << endl; 555 | } 556 | } 557 | ``` 558 | 559 | 560 | 561 | **Output String streams** 562 | 563 | Use an output string stream object, `ostringstream`, to build up a string from a variety of other types. 564 | 565 | Example: buildString.cc 566 | 567 | ```cpp 568 | #include 569 | #include 570 | #include 571 | using namespace std; 572 | 573 | int main () { 574 | ostringstream ss; 575 | int lo {1}, hi {100}; 576 | ss << "Enter a # between " << lo << " and " << hi; 577 | string s {ss.str()}; 578 | cout << s << endl; 579 | } 580 | ``` 581 | 582 | 583 | 584 | ## Functions 585 | 586 | Basic format: 587 | 588 | ```cpp 589 | return-type function-name( type1 arg1, type2 arg2, ... ) { 590 | return value of appropriate type; 591 | } 592 | ``` 593 | 594 | Using `-wall` when compiling your program will warn you of this error. 595 | 596 | ### Forward declarations 597 | 598 | Separate a function into **declaration**(signature) and its **definition**(implementation). 599 | 600 | Example: forwardGood.cc 601 | 602 | ```cpp 603 | 604 | #include 605 | #include 606 | using namespace std; 607 | 608 | bool odd(unsigned int n); // foward declare it here so we can use in the next function 609 | 610 | bool even(unsigned int n) { 611 | if (n == 0) return true; 612 | return odd(n - 1); 613 | } 614 | 615 | bool odd(unsigned int n) { 616 | if (n == 0) return false; 617 | return even(n - 1); 618 | } 619 | 620 | int main() { 621 | cout << boolalpha << even(3) << " " << even(4) << " " 622 | << odd(3) << " " << odd(4) << endl; 623 | } 624 | ``` 625 | 626 | 627 | 628 | ### Default Function Parameters 629 | 630 | An argument may or may not be supplied to that parameter. 631 | 632 | ```cpp 633 | void printSuiteFile(string name = "suite.txt") { 634 | // "suite.txt" is a default value for parameter name 635 | ifstream file{name}; 636 | string s; 637 | while (file >> s) cout << s << endl; 638 | } 639 | 640 | printSuiteFile(); // prints from suite.txt 641 | printSuiteFile("suite2.txt"); // prints from suite2.txt 642 | ``` 643 | 644 | **Note**: optional parameters must be the *last* parameters. Or else it is ambiguous. 645 | 646 | int addStuff(int x=5, int y, int z=10) {...} 647 | 648 | addStuff(7,2); // x=7, y=2, z=10? 649 | 650 | addStuff(7,2); //x=5, y=7, z=2 651 | 652 | The default values are, by convention, usually listed in the function declaration and not in the function definition. They cannot occur in both locations! 653 | 654 | 655 | 656 | ## Overloading 657 | 658 | in C: 659 | 660 | ```c 661 | int negInt(int a) { return -a; } // function to negate an int 662 | bool negBool(bool b) { returen !b; } // functino to negate a bool 663 | ``` 664 | 665 | 666 | 667 | in C++: Functions with difference parameter lists can share the *same* name 668 | 669 | ```cpp 670 | int neg(int a) { return -a; } 671 | bool neg(bool b) { return !b; } 672 | ``` 673 | 674 | It's fine to do this! 675 | 676 | This is called **overloading**. 677 | 678 | The compiler uses the number and types of arguments to decide which neg is being called (i.e. decision is made at compile time). Overloads must differ in *number or type of parameters*, just differing in return type is not enough (the compiler won't distinguish based on return types. 679 | 680 | We've seen overloading already: `<<, >>` are overloaded; their behaviour depends on the types of their arguments. 681 | 682 | 683 | 684 | ## Structures 685 | 686 | A structure for a Node: 687 | 688 | ```cpp 689 | struct Node { 690 | int data; 691 | Node *next; // pointer to the next node 692 | }; // semicolon at the end of declaring a structure 693 | 694 | Node *head = nullptr; 695 | Node n{5, nullptr}, n1 = {6, head}; 696 | ``` 697 | 698 | **Note**: in C we would have to say `struct Node *next; `, in C++ we don't need the 699 | 700 | `struct`. Don't forget the semiclon! C++ no longer uses NULL, should be using `nullptr` instead. 701 | 702 | If a node stores a node, it will have an infinite size. The compiler needs to know how much space to allocate before compiling. Hence, we *must* have a pointer pointing to the next node. 703 | 704 | A pointer just contains a memory address, and the size of memory addresses is known at compilation. 705 | 706 | 707 | 708 | 709 | 710 | ## Constants 711 | 712 | ```cpp 713 | const int maxGrade = 100; // Must be initialized when it is defined 714 | ``` 715 | 716 | Declare as many things const as you can, it helps catch errors. 717 | 718 | ```cpp 719 | Node n1 {5, nullptr}; 720 | // nullptr is the syntax in C++ for null pointers, do NOT use NULL or 0 in this class! 721 | const Node n2 = n1; 722 | // defining a new node n2 which is an immutable(const) copy of n1 723 | 724 | ``` 725 | 726 | 727 | 728 | 729 | 730 | ## Parameter passing 731 | 732 | Recall: 733 | 734 | ```c 735 | void inc(int n) { ++n; } 736 | ... 737 | int x = 5; 738 | inc(x); 739 | cout << x << endl; // prints 5, why? 740 | ``` 741 | 742 | **Call-by-value**: inc gets a *copy* of x and increments the copy. (The copy will be modified and the original will be unchanged) 743 | 744 | Solution: if a function needs to mutate an argument, pass a pointer. 745 | 746 | ```cpp 747 | void inc(int *n) { ++*n; } // notice the user of pointer and derefercing here 748 | ... 749 | int x = 5; 750 | inc(&x); // x's address is passed by value; inc changes the data at the address of x 751 | cout << x << endl; // prints 6 (changes the value of parameter) 752 | ``` 753 | 754 | 755 | 756 | Question: why can we say `cin >> x` and not `cin >> &x` ? 757 | 758 | Answer: C++ has another pointer-like type: **references** 759 | 760 | 761 | 762 | ## References (very important!) 763 | 764 | The general rule is to pass information by reference, because you don't need to remember to dereference your pointers all the time. 765 | 766 | ```cpp 767 | int y = 10; 768 | int &z = y; // a reference to z 769 | // the & in a type means a reference; the & in an operator means the address of the operator 770 | // the type of z is (int &) 771 | // z is an lvalue reference to an int, behaves LIKE a const ptr, BUT a reference is NOT a pointer 772 | ``` 773 | 774 | Note: the ampersand `&` in a type means a reference, the `&` as an operator means the address of the operator (e.g. &operator=, the copy assignment operator) 775 | 776 | A reference is NOT a pointer, they are distinct types! 777 | 778 | A references is an lvalue that acts *like* constant pointers with automatic dereferencing. 779 | 780 | ```cpp 781 | int y = 10; 782 | int &z = y; // z is an lvalue reference 783 | z = 12; // Note: NOT *z = 12; now, y is equal 12 784 | cout << y << endl; // prints 12 785 | ``` 786 | 787 | In ALL cases, a reference will behave exactly like the data it refers to. 788 | 789 | ```cpp 790 | int *p = &z; // Taking the address of z, gives the address of y (the thing it refers to) 791 | // z refers to y, so the address of z: &z, refers to the address of y 792 | ``` 793 | 794 | In other words, z is an **alias** for y. (z is another name for y). This means, z is another name for y, and z behaves like y in all circumstances. 795 | 796 | A **reference** is an **alias** for an already existing variable, it cannot be changed to refer to another variable, hence it behaves similar to a constant pointer. 797 | 798 | 799 | 800 | Since it is a constant, it must be initialized when it is defined. 801 | 802 | There are **rvalues** and **lvalues**. If you can take the address of an expression, it is an **lvalue**; otherwise, it's an **rvalue**. 803 | 804 | Thank of an lvalue as something that can appear on the left-hand side of an assignment statement. For example: 805 | 806 | ```cpp 807 | int x = 5; 808 | int * ptr = nullptr; 809 | // x and ptr are both lvalues 810 | ``` 811 | 812 | 813 | 814 | What is a **reference**? A **reference** is an lvalue that acts like a constant pointer but the compiler automatically dereferencs it. Since it is constant, it **must** be initialized when defined. 815 | 816 | ```cpp 817 | int x = 5; 818 | int &y = x; // type of y is (int &) 819 | int *ptr = &y; // &y is the address of x, so ptr points to address of x 820 | y += 2; 821 | *ptr += 3; // ptr contains the address of x 822 | cout << x << endl; 823 | ``` 824 | 825 | 826 | 827 | Things you **can't do** with lvalue references: 828 | 829 | - leave them uninitialized (e.g. int &x) 830 | - Must be initialized with something that has an address (lvalue) 831 | - `int &x = 3;` // Not allowed, 3 does not have an address, it is an rvalue 832 | - `int &x = y + z; ` // Not allowed, the return value of y+z is not stored somewhere in our program and does not have an address 833 | - `int &x = y;` // Ok (as long as y is defined) 834 | 835 | - Cannot create a pointer to a reference, (e.g. int &*x). BUT can have a reference to a pointer (e.g int *&x = ...;) 836 | - Cannot create a reference to a reference (e.g. int &&r). Actually this has another meaning 837 | - Cannot create an array of references 838 | - e.g., int &r[3] = {n, n, n}; 839 | 840 | By the rules of C++. references are not guaranteed to have any memory themselves. (They don't always take memory) 841 | 842 | 843 | 844 | Create a reference to an integer pointer: 845 | 846 | ```cpp 847 | int *& refPtr = ptr; // read form right-to-left 848 | ``` 849 | 850 | 851 | 852 | 853 | 854 | What can you do with references? Passing as function parameters, or the return type of a function: 855 | 856 | ```cpp 857 | void in(int &n) { 858 | ++n; // No dereference here, n is a reference to the argument provided 859 | } 860 | 861 | int x = 5; 862 | inc(x); 863 | cout << x << engl; // prints 6 864 | ``` 865 | 866 | Why does `cin >> x` work? Because it takes x by reference: 867 | 868 | ```cpp 869 | std::istream &operator >> (std::istream &in. int &n); 870 | // the prototype of an input operator 871 | ``` 872 | 873 | 874 | 875 | 876 | 877 | ### Pass-by-value 878 | 879 | **Pass-by-value**: int f(int n) {...} copies the argument 880 | 881 | If the parameter is of a large type, this copy could be expensive 882 | 883 | ```cpp 884 | struct ReallyBig {...}; // struct that holds a *lot* of information 885 | 886 | int f(ReallyBig rb) {...} // Copies rb, potentially slow. See move.copy ellision 887 | 888 | int g(ReallyBig &rb) {...} // Alias, fast, BUT caller might accidentally change the value supplied 889 | 890 | int h(const ReallyBig &rb) {...} // take a constant reference, fast, not copt, parameter cannot be changed - no unintented side-effects. 891 | ``` 892 | 893 | **Advice**: prefer pass-by-const-ref over pass-by-value for anything larger than integer, UNLESS the function needs to make a copy anyways, then possibly use pass-by-value. 894 | 895 | 896 | 897 | Also: 898 | 899 | ```cpp 900 | int f(int &n) {...} // cant bind non-const lvalue reference to an rvalue (5) 901 | int g(const int &n) {...} 902 | 903 | f(5); // NOT allowed, cannot bind the lvalue reference n to the literal value 5. If n changes, it can't change the literal value 5 904 | g(5); // OK because we promised the compiler we wouldn't change it, so we get away with it. 905 | ``` 906 | 907 | How is it that g(5) works? The compiler creates a *temporary* location in memory to store the number 5, and that way n has something to refer to. 908 | 909 | 910 | 911 | ## Dynamic Memory Allocation 912 | 913 | C: 914 | 915 | ```cpp 916 | int *p = malloc(... *sizeof(int)); 917 | ... 918 | free(p); 919 | ``` 920 | 921 | DO NOT use these in C++, Instead, use `new` and `delete`, they are type aware, less error prone, and more convenient 922 | 923 | 924 | 925 | ```cpp 926 | struct Node { 927 | int data; 928 | Node *next; 929 | } 930 | 931 | Node *np = new Node; 932 | // np is a varianle on the stack, it's a pointer variable and it stores the memory address of a Node that we just created that is stored on the heap 933 | delete np; 934 | ``` 935 | 936 | - All local variables reside on the stack 937 | 938 | - variables are deallocated when they go out of scope (stack is popped) 939 | 940 | - allocated memory (with new) resides on the *heap* 941 | 942 | - allocated memory remains allocated until it is explicitly freed with delete 943 | 944 | - If you don't delete all allocated memory: **memory leak** 945 | 946 | Programs will eventually fail. Any memory leak results in incorrected program 947 | 948 | 949 | ```cpp 950 | #include 951 | 952 | struct Node { 953 | int data; 954 | Node *next; 955 | }; 956 | 957 | int main() { 958 | Node n{ 5, nullptr }; 959 | Node * np = new Node{ 3, &n }; 960 | std::cout << n.data << ' ' << np->data << std::endl; 961 | delete np; 962 | } 963 | ``` 964 | 965 | 966 | 967 | ### Array Forms 968 | 969 | ```cpp 970 | Node *myNodes = new Node[10]; 971 | // An array of 10 nodes on the heap 972 | // myNodes is a stack-allocated pointer to the first node in that array 973 | ... 974 | delete []myNodes; // Note the square brackets 975 | ``` 976 | 977 | 978 | 979 | It is important to match the correct form of delete with the corresponding new. 980 | 981 | If memory was allocated with the ordinary `new` then it must be deallocated with the ordinary `delete`. 982 | 983 | If the memory was allocated with the array form `new`. then it must be deallocated with the array form `delete[]`. 984 | 985 | Mixing the two forms of new and delete results in undefined behaviour. (the compiler can do anything at once) 986 | 987 | 988 | 989 | ### Returning by value/pointer/reference 990 | 991 | There are three ways in which we can return information from a function in C++: 992 | 993 | 1. return by *value* 994 | 995 | 2. return by *pointer* 996 | 997 | 3. return by *reference* 998 | 999 | 1000 | 1001 | ```cpp 1002 | Node getMeANode() { 1003 | Node n; 1004 | return n; 1005 | // possibly expensive: n is copied to the caller's stack frame on return 1006 | } 1007 | 1008 | // What about returning a pointer (or reference) instead? 1009 | Node *getMeANode() { 1010 | Node n; 1011 | return &n; // returning a pointer to node n 1012 | } 1013 | 1014 | // As soon as the function returns, n no longer exists, 1015 | // so returning it's address is returning an address to data 1016 | // that's dead on return (dangling pointer) 1017 | 1018 | Node *getMeANode() { 1019 | Node *np = new Node{0, nullptr}; 1020 | return np; 1021 | } 1022 | 1023 | // This is okay - returns a pointer to heap data that will persist after this function returns, but the caller is now responsible for deleting it 1024 | 1025 | ... 1026 | Node n = getMeANode(); 1027 | ... 1028 | delete n; 1029 | ``` 1030 | 1031 | Which should you pick? Most times return by value - it is not as expensive as it looks. 1032 | 1033 | What about return by reference? 1034 | 1035 | ```cpp 1036 | Node &getMeANode() { 1037 | Node n{0, nullptr}; 1038 | return n; 1039 | } 1040 | 1041 | // EXACT same problem as returning a pointer here, (dangling ptr) 1042 | // we have a reference to memory that gets destroyed as 1043 | // soon as this runction returns, the receipient is receiving a reference to 1044 | // local variable that no longer exists once the function ends 1045 | 1046 | 1047 | Node &getMeANode() { 1048 | Node *n = new Node{0, nullptr}; 1049 | return *n; // return dereferenced n 1050 | } 1051 | 1052 | // Sure - BUT this is a bit tricky for the caller, the caller have to delete it 1053 | 1054 | // if the caller says 1055 | Node n = getMeANode(); 1056 | // This is a Instant memory leak! n is a local copy of the node reference that was allocated on the heap - no longer have the address to it. 1057 | 1058 | Node &n = getMeANode(); 1059 | // Now n is a reference to that heap allocated now 1060 | ... 1061 | delete &n; // will give us the address of the heap-allocated memory to free 1062 | ``` 1063 | 1064 | In general, avoid doing this. If you're going to return a reference it should usually be because it's something you already have a reference to (like a parameter that was passed in). 1065 | 1066 | 1067 | 1068 | ### Optional Files 1069 | 1070 | The convention in Linux to use square brackets in the documentation is to show that these command-line arguments are optional. 1071 | 1072 | ```bash 1073 | ./a.out [ input-file-name [ output-file-name ] ] 1074 | ``` 1075 | 1076 | - if two command line arguments are present, the first is the name of the input file to use, the second is the output file 1077 | - if only one command line argument is present, it is the name of the input file to use, the output goes to stdout 1078 | - if no arguments are present, the program reads from stdin and writes to stdout 1079 | 1080 | 1081 | 1082 | In C++, we modify the `main` program to show it now has two arguments, first is the number of arguments (int), second is the array of NULL terminated C-style string (char*). 1083 | 1084 | ```cpp 1085 | ./ a.out "abc def" 123 1086 | 1087 | int main(int argc, char *argv[]) { 1088 | ... 1089 | } 1090 | 1091 | // here argc = 3, and argv will be an array of 4 C strings, where the fianl element is the C equivalent to the C++ nullptr. 1092 | // each element of argv is a pointer to a null-terminated array of characters, i.e. a C string 1093 | ``` 1094 | 1095 | 1096 | 1097 | We learned how to overload functions, and we can also overload operators. 1098 | 1099 | ## Operator Overloading 1100 | 1101 | We can give meanings to C++ operators for types we create 1102 | 1103 | ```cpp 1104 | struct Vec { 1105 | int x, y; 1106 | }; 1107 | 1108 | Vec operator+(const Vec &v1, const Vec &v2) { 1109 | Vec v{v1.x + v2.x, v1.y + v2.y}; 1110 | return v; 1111 | } 1112 | 1113 | // Scalar multiplication 1114 | 1115 | Vec operator*(const int k, const Vec &v) { 1116 | return {k*v1.x, k*v1.y}; 1117 | // Okay because the compiler knows its a Vec based on the 1118 | // return type and constructs it from these values 1119 | } 1120 | 1121 | ... 1122 | 1123 | Vec v{3, 4}; 1124 | Vec w = 5 * v; // Calls the function above 1125 | Vec z = v + w; 1126 | Vec q = v * 10; // Doesn't work!Doesn't match the parameters above! 1127 | 1128 | // Just make another function 1129 | Vec operator*(const Vec &v, const int k) { 1130 | return k*v; // just call the other previously defined operator* 1131 | } 1132 | 1133 | ``` 1134 | 1135 | 1136 | 1137 | 1138 | 1139 | ### Overloading >> and << 1140 | 1141 | ```cpp 1142 | struct Grade { 1143 | int theGrade; 1144 | }; 1145 | 1146 | ostream &operator<<(ostream &out, const Grade &g) { 1147 | return out << g.theGrade << '%'; // return the output stream 1148 | } 1149 | 1150 | istream &operator>>(istream &in, Grade &g) { 1151 | in >> g.theGrade; 1152 | if (g.theGrade < 0) g.theGrade = 0; 1153 | if (g.theGrade > 100) g.theGrade = 100; 1154 | return in; // return the input stream back 1155 | } 1156 | ``` 1157 | 1158 | Rules for writing an output operator: 1159 | 1160 | 1. The return type is always (std::ostream &) 1161 | 2. The function name is always operator << 1162 | 3. The first parameter is always the output stream (std::ostream&) 1163 | 4. The second parameter is always the information being output. If it's the size of an integer or smaller, you can pass it by value, but it's usually a constant reference 1164 | 5. Before you do anything else, write the return statement to return whatever the name of the output stream is. 1165 | 1166 | ```cpp 1167 | std::ostream &operator<<(std::ostream &out, const typeToPrint &value) { 1168 | out << v; // whatever is appropriate for the type you are outputting 1169 | return out; 1170 | } 1171 | ``` 1172 | 1173 | The input operator changes both the input stream since it is consuming information from it. The second parameter that is supposed to hold the information read in from the input stream. This should imply to you that both parameters need to be passed as references, and not as constant reference. 1174 | 1175 | ```cpp 1176 | std::istream &operator>>(std::istream &in, Grade &g) { 1177 | in >> g.theGrade; 1178 | if (g.theGrade < 0) g.theGrade = 0; 1179 | else if (g.theGrade > 100) g.theGrade = 100; 1180 | return in; 1181 | } 1182 | ``` 1183 | 1184 | Rules for writing an input operator: 1185 | 1186 | 1. The return type is always (std::istream &) 1187 | 2. The function name is always operator>> 1188 | 3. The first parameter is always the input stream (std::istream &) 1189 | 4. The second parameter is always the information being read in. It will be a reference 1190 | 5. Before you do anything else, write the return statement to return whatever the name of the input stream is. 1191 | 1192 | ```cpp 1193 | std::istream &operator>>(std::istream &in, typeToRead &value) { 1194 | in >> v; // whatever is appropriate for the type you are reading in 1195 | return in; 1196 | } 1197 | ``` 1198 | 1199 | 1200 | 1201 | 1202 | 1203 | 1204 | 1205 | 1206 | 1207 | ## The Preprocessor 1208 | 1209 | Both C and C++ used a tool called the **C preprocessor**, which is tasked with handling **preprocessor directives** like `#include`. 1210 | 1211 | The preprocessor's primary purpose is to control what code is included in the program. 1212 | 1213 | Transforms the program *before* the compiler sees it. 1214 | 1215 | ```cpp 1216 | #_______ - Preprocessor directive 1217 | e.g. #include 1218 | ``` 1219 | 1220 | Including old C header: new naming convention. 1221 | 1222 | Instead of e.g.` #include `, use `#include `. 1223 | 1224 | 1225 | 1226 | To run only the preprocessor, use the `-E` flag to `g++`, the preprocessor will output to standard out. 1227 | 1228 | ```bash 1229 | $ g++ -std=c++14 -E name.cc 1230 | ``` 1231 | 1232 | 1233 | 1234 | ### Constant definition 1235 | 1236 | ```cpp 1237 | #define VAR VALUE 1238 | ``` 1239 | 1240 | sets a preprocessor variable with the name VAR. The preprocessor will replace all occurrences of VAR in the source file with VALUE (Except for those in quoted strings). 1241 | 1242 | Example: 1243 | 1244 | ```cpp 1245 | #define MAX 10; 1246 | int x[MAX]; 1247 | 1248 | // would be transformed to: 1249 | int x[10]; 1250 | 1251 | // However, if you want to define for an array length, you should just have that to be a constant variable, but not a preprocessor 1252 | ``` 1253 | 1254 | This is mostly unneeded, it is more type-safe to use `const ` definitions. 1255 | 1256 | 1257 | 1258 | ### Conditional compilation 1259 | 1260 | We can combine constant definitions with conditional compilation, another preprocessor feature. 1261 | 1262 | 1263 | If you are writing a program that need to be build on both Linux or Windows. 1264 | 1265 | ```cpp 1266 | #define OS Unix 1267 | ... 1268 | #if OS == Unix // this is a preprocessor if, happens before the compiler sees it 1269 | #include 1270 | #elif OS == Windows 1271 | #include 1272 | #endif 1273 | ``` 1274 | 1275 | `#if` supports `#elif` and `#else`. 1276 | 1277 | ```cpp 1278 | #define SECURITYLEVEL 1 1279 | #if SECURITYLEVEL == 1 1280 | short int 1281 | #elif SECURITYLEVEL == 2 1282 | long long int 1283 | #endif 1284 | publicKey; 1285 | ``` 1286 | 1287 | It is common to use `#define` to add debugging to the code and easily remove it. 1288 | 1289 | 1290 | 1291 | Now: you can also just define a flag 1292 | 1293 | ```cpp 1294 | #define FLAG 1295 | ``` 1296 | 1297 | defines the preprocessor variable with that name, its value will be the empty string 1298 | 1299 | 1300 | 1301 | ### Preprocessor definitions in the command line 1302 | 1303 | We can define preprocessor symbols by compiler arguments. 1304 | 1305 | The argument is `-DVAR=VALUE`, where *VAR* is the name to define and *VALUE* is the value to set. 1306 | 1307 | ```cpp 1308 | preproc.cc 1309 | #include 1310 | using namespace std; 1311 | 1312 | int main() { 1313 | cout << X << endl; 1314 | } 1315 | 1316 | // compile it 1317 | g++14 -DX="\"Hello world\"" preproc.cc 1318 | 1319 | >>> Hello world 1320 | ``` 1321 | 1322 | 1323 | 1324 | We can use this to print debug statements. `#ifdef` checks if a preprocessor variable is set at all, rather than checking its exact value. 1325 | 1326 | ```cpp 1327 | #ifdef NAME/ #ifndef NAME: true if the preprocessor variable with NAME has/has not been defined 1328 | ``` 1329 | 1330 | 1331 | 1332 | ```cpp 1333 | int main() { 1334 | #ifdef DEBUG 1335 | cout << "Setting x = 1" << x << endl; 1336 | #endif 1337 | int x = 1; 1338 | while (x < 10) { 1339 | ++x; 1340 | #ifdef DEBUG 1341 | cout << "x is now: " << x << endl; 1342 | #endif 1343 | } 1344 | } 1345 | 1346 | 1347 | // compile: 1348 | g++ -DDEBUG debug.cc 1349 | >> Setting x = 1 1350 | >> x is now: 2 1351 | >> x is now: 3 1352 | >> x is now: 4 1353 | ... 1354 | >> x is now: 10 1355 | ``` 1356 | 1357 | The opposite of `#ifdef` is `#ifndef`. However, these is no `#else` for `#ifdef`. 1358 | 1359 | 1360 | 1361 | ### Preprocessor commenting 1362 | 1363 | Special case: `#if 0 ... #endif`. `#if` 0 is never true, so all of the text inside the if will be removed before the compiler sees it. 1364 | 1365 | Think of functions as a heavy-duty "comment out" of large segments of code, since `#if 0` nests properly. 1366 | 1367 | 1368 | 1369 | 1370 | 1371 | 1372 | 1373 | ## Separate Compilation 1374 | 1375 | Split programs up into composable modules, which each provide: 1376 | 1377 | - **interface** (header): type definitions, and prototypes (declarations) for functions (.h file) 1378 | - **implementation**: full definition for every provided function (.cc file) 1379 | 1380 | 1381 | 1382 | Recall: 1383 | 1384 | - **declaration**: asserts something exists (function or variable, but doesn't define its content) 1385 | - **definition**: full details of how something should work, allocates space (for variables and functions) 1386 | 1387 | You can declare something as many times as you want - you can define things ONLY ONCE 1388 | 1389 | Example: 1390 | 1391 | This is our header file: 1392 | 1393 | ```cpp 1394 | // vec.h 1395 | // This is a full definition of type Vec, we can't define Vec again 1396 | struct Vec { 1397 | int x,y; 1398 | } 1399 | 1400 | // This is a declaration 1401 | Vec operator+(const Vec &v1, const Vec &v2); 1402 | // parameter names can be omitted in declarations 1403 | ``` 1404 | 1405 | This is our implementation 1406 | 1407 | ```cpp 1408 | // main.cc (client code - USES our module) 1409 | #include "vec.h" 1410 | int main() { 1411 | Vec v{1,2}; 1412 | v = v + v; 1413 | } 1414 | 1415 | // vec.cc (implementation) 1416 | #include "vec.h" 1417 | Vec operator+(const Vec &v1, const Vec &2) { 1418 | return {v1.x + v2.x, v1.y + v2.y}; 1419 | } 1420 | 1421 | // To compile: 1422 | g++14 -c main.cc 1423 | // We will get 1424 | main.cc main.o vec.cc vec.h 1425 | g++14 -c vec.cc 1426 | // We will get 1427 | main.cc main.0 vec.cc vec.h vec.o 1428 | // Now to link these objects 1429 | g++14 vec.o main.o -o main 1430 | // Now we have the executable 1431 | main 1432 | ``` 1433 | 1434 | 1435 | 1436 | **Compiling separately**: 1437 | 1438 | `g++ -c vec.cc` compile only -- do not link 1439 | 1440 | `g++ -c main.cc` produces an object (.o) file 1441 | 1442 | `g++ vec.p main.o -o main` links our objects into the executable 1443 | 1444 | What happens if we change vec.cc? Only needs to recompile vec.cc and then relink. 1445 | 1446 | ```bash 1447 | g++ -c vec.cc 1448 | g++ vec.o main.o -o main 1449 | ``` 1450 | 1451 | The `-c` option requests g++ that a `.cc` file be compiled into a `.o` file, or an **object file**. An object file contains compiled code, but not enough for a full program. In this case, main.o contains `main` (from main.cc), and vec.o contains the `+`operator for Vecs (from vec.cc). 1452 | 1453 | You can use .o files like .cc files when outputting a program, combining multiple .o files to create a program is called **linking**. 1454 | 1455 | Since .h files contain no code, only declarations, they shouldn't be compiled at all! 1456 | 1457 | 1458 | 1459 | What happens if we change vec.h? Well then since main.cc and vec.cc both include vec.h, they both must be *recompiled*, and then *relinked*. 1460 | 1461 | How can we keep track of dependencies of modules and perform minimal compilation? 1462 | 1463 | 1464 | 1465 | 1466 | 1467 | ## Make and Makefile 1468 | 1469 | The relationship between the files created a system of **dependencies**. 1470 | 1471 | ```mermaid 1472 | graph LR; 1473 | 1474 | A((vecs))-->B1((main.o)) 1475 | A-->A1((vec.o)) 1476 | B1-->B2((main.cc)) 1477 | A1-->A2((vec.cc)) 1478 | B2-->A0((vec.h)) 1479 | A2-->A0 1480 | ``` 1481 | 1482 | 1483 | 1484 | Linux tool: **make** 1485 | 1486 | Create a Makefile that says which files depend on which other files, and how to build them. 1487 | 1488 | ```bash 1489 | Makefile # must places in a file names "Makefile" 1490 | 1491 | main: main.o vec.o 1492 | # this must be a tab character, or else make won't work 1493 | g++ main.o vec.o -o main 1494 | 1495 | main.o: main.cc vec.h 1496 | g++ -std=c++14 -c main.cc 1497 | 1498 | vec.o: vec.cc vec.h 1499 | g++ -std=c++14 -c vec.cc 1500 | 1501 | # fake optional target 1502 | .PHONY: clean 1503 | 1504 | clean: 1505 | rm *.o main # remove everything the compile done 1506 | ``` 1507 | 1508 | The lines not indented here are *dependencies*, they show that the file before the colon depends on the file(s) after the colon. 1509 | 1510 | Each file is a **target**, which is something this Makefile describes how to create. 1511 | 1512 | The command to create any given target is called a **recipe** (the lines that starts with g++, which are executed as shell commands). 1513 | 1514 | 1515 | 1516 | Makefiles are very particular about their whitespace, you need to be using *tabs* for whitespaces. 1517 | 1518 | In general, the command is: 1519 | 1520 | `make target` --- builds the requested target 1521 | 1522 | To use the Makefile, just run the make command: 1523 | 1524 | `make` 1525 | 1526 | then it builds the first target in the makefile. Recursively build any dependencies if necessary. 1527 | 1528 | Make compares timestamps, if a target is older than any of its dependencies then it must be rebuilt. 1529 | 1530 | 1531 | 1532 | ### Phony Targets 1533 | 1534 | We need commands to clean up after make. This is done through a **phony target**, i.e. a target that exists only for its recipe, and doesn't actually build for anything. The `clean` phony target is commonly used for cleanup commands. 1535 | 1536 | ```bash 1537 | .PHONY: clean 1538 | 1539 | clean: 1540 | rm *.o vecs 1541 | 1542 | 1543 | $ make clean 1544 | rm *.o vecs 1545 | $ 1546 | ``` 1547 | 1548 | 1549 | 1550 | 1551 | 1552 | ### Make Variables 1553 | 1554 | Generalize out makefile with **make variables**: 1555 | 1556 | ```bash 1557 | CXX=g++ # name for compiler 1558 | CXXFLAGS=-std=c++14 -Wall # the space between assignments are important! 1559 | OBJECTS=main.o vec.o # define object files 1560 | EXEC=main 1561 | 1562 | ${EXEC}: ${OBJECTS} 1563 | ${CXX} ${OBJECTS} -o ${EXEC} 1564 | 1565 | main.o: main.cc vec.h 1566 | # I can omit the recipe, make assumes that the recipe for a .o file is: 1567 | # ${CXX} ${CXXFLAGS} -c _____.c 1568 | 1569 | vec.o: vec.cc vec.h 1570 | 1571 | .PHONY: clean 1572 | 1573 | clean: 1574 | rm ${OBJECTS} ${EXEC} 1575 | ``` 1576 | 1577 | The biggest problem we wanted to solve was keeping all over dependencies straight, with this makefile we still need to include all the dependencies for files we want to build. Wouldn't it be nice if make could somehow figure these out automatically? 1578 | 1579 | ### Automatic Dependency Management 1580 | 1581 | We can get some help from g++ 1582 | 1583 | `g++ -MMD -c vec.cc` creates a vec.o and a vec.d, where `vec.d` includes: `vec.o: vec.cc vec.h` (what vec.o depends on), (.d files are make dependencies). 1584 | Include this in our makefile: 1585 | 1586 | ```bash 1587 | CXX=g++ 1588 | CXXFLAGS=-std=c++14 -Wall -MMD 1589 | OBJECTS=main.o vec.o 1590 | DEPENDS=${OBJECTS:.o=.d} # replace .o with .d 1591 | EXEC=main 1592 | 1593 | ${EXEC}: ${OBJECTS} 1594 | ${CXX} ${OBJECTS} -o ${EXEC} 1595 | 1596 | -include ${DEPENDS} # the '-' means: the these files don't exist, don't worry about it 1597 | .PHONY: clean 1598 | 1599 | clean: 1600 | rm ${OBJECTS} ${DEPENDS} ${EXEC} 1601 | ``` 1602 | 1603 | Now, as our project expands, all we have to do is add the .o files ot the OBJECTS variable in the Makefile. 1604 | 1605 | 1606 | 1607 | ### Global Variables 1608 | 1609 | What if we want a module to provide a global variable? 1610 | 1611 | ```cpp 1612 | // abc.h 1613 | int globalNum; 1614 | // WRONG, this is a declaration AND a definition of the variable globalNum, so all files that include this will define a variable named globalNum 1615 | ``` 1616 | 1617 | This is because *defining* a variable allocates space for it, so here we have defined the space twice. 1618 | 1619 | We need some way to say "JUST declare this variable, it will be defined somewhere else that the linker can find". 1620 | **Solution**: place the variable in a cc file, and simply *declare* it here with the **extern modifier** 1621 | 1622 | ```cpp 1623 | // abc.h 1624 | extern int globalNum; // declaration but NOT a definition 1625 | 1626 | // abc.cc 1627 | int globalNum; // definition, will be linked when linking occurs 1628 | ``` 1629 | 1630 | Note that the `extern` declaration has no initializer. It can't have one, since it doesn't actually allocate the variable, and so can't give it a value. Hence, we still need the actual variable declaration, put it in the .cc file so that it is not allocated multiple times. 1631 | 1632 | This pattern is the same for all global variables which are used in multiple files. Since the header file can only have declarations, we use `extern` there, but the actual variable must still be defined and allocated in an implementation file. 1633 | 1634 | 1635 | 1636 | ## Preprocessor Guards 1637 | 1638 | Suppose we want to write a linear algebra module: 1639 | 1640 | ```cpp 1641 | // linalg.h 1642 | #include "vec.h" 1643 | #include 1644 | 1645 | // NEVER NEVER NEVER use "using namespace std" in a header file. It forces that on whoever includes your header, and they may not want it 1646 | // they may have names that clash with things in the std namespace 1647 | 1648 | std::ostream& operator<<(std::ostream&, const Vec &); 1649 | 1650 | 1651 | // linalg.cc 1652 | #include "vec.h" 1653 | #include "linalg.h" 1654 | #include 1655 | using namespace std; 1656 | 1657 | ostream& operator<<(ostream &out, const Vec& v) { 1658 | cout << "{" << v.x << ", " << v.y << "}"; 1659 | } 1660 | 1661 | 1662 | // main.cc 1663 | #include "vec.h" 1664 | #include "linalg.h" 1665 | #include 1666 | using namespace std; 1667 | 1668 | int main() { 1669 | Vec v{1, 2}; 1670 | v = v + v; 1671 | cout << v << endl; 1672 | } 1673 | 1674 | ``` 1675 | 1676 | Consider our linalg module: 1677 | 1678 | ```cpp 1679 | // linalg.h 1680 | #include "vec.h" 1681 | ... 1682 | 1683 | // linalg.cc 1684 | #include "linalg.h" 1685 | #include "vec.h" 1686 | ... 1687 | 1688 | // main.cc 1689 | #include "vec.h" 1690 | #include "linalg.h" 1691 | ... 1692 | ``` 1693 | 1694 | This doesn't compile - why? main.cc and linlag.cc both include vec.h and linalg,h. But linalg.h includes vec.h, so each of main.cc and linalg.cc include vec.h twice. That means they get TWO definitions of the type Vec. You can only define something ONCE. 1695 | so how can we fix this? 1696 | 1697 | We need to prevent files from being included more than once. 1698 | **Solution**: Use a **preprocessor guard**, also called **#include guard** 1699 | 1700 | ```cpp 1701 | #ifndef SOME_UNIQUE_MACRO_NAME_H 1702 | #define SOME_UNIQUE_MACRO_NAME_H 1703 | ... 1704 | #endif 1705 | ``` 1706 | 1707 | 1708 | 1709 | ```cpp 1710 | // vec.h 1711 | 1712 | #ifndef VEC_H 1713 | #define VEC_H 1714 | struct Vec { 1715 | int x, y; 1716 | } 1717 | 1718 | Vec operator+(const Vec &, const Vec &); 1719 | #endif 1720 | 1721 | 1722 | // linalg.h 1723 | #ifndef LINALG_H 1724 | #define LINALG_H 1725 | #include "vec.h" 1726 | #include 1727 | 1728 | 1729 | 1730 | std::ostream& operator<<(std::ostream&, const Vec &); 1731 | #endif 1732 | ``` 1733 | 1734 | ```cpp 1735 | #include guard: 1736 | 1737 | #ifndef VEC_H 1738 | #define VEC_H 1739 | ... // file contents 1740 | #endif 1741 | ``` 1742 | 1743 | The firts time vec.h is included, the symbol VEC_H is not defined, we then immediately define when the file is included. 1744 | So, subsequent inlcudes see that this variable has already been defined and are thus suppressed by the preprocessor. 1745 | 1746 | **EVERY** .h file should inlcude a header guard. 1747 | **NEVER**: put `using namespace std;` in headers, this forces this directive on clients that use your module, but they may have conflicts. 1748 | 1749 | 1750 | **NEVER EVER** compile .h files, their contents get compiled as a result of compiling the .cc files that include them 1751 | 1752 | **NEVER EVER** include .cc files! Implementation files are to be compiled only, never included. 1753 | 1754 | 1755 | 1756 | 1757 | 1758 | -------------------------------------------------------------------------------- /5. Encapsulation and Introduction to Design Patterns.md: -------------------------------------------------------------------------------- 1 | # Encapsulation and Introduction to Design Patterns 2 | 3 | 4 | 5 | # 5.1 Invariants and Encapsulation (Feb 15) 6 | 7 | ## Invariants 8 | 9 | Considet the linked list class: 10 | 11 | ```cpp 12 | struct Node { 13 | int data; 14 | Node *next; 15 | Node(int data, Node *next) : data{data}, next{next} {} 16 | ~Node() { delete next; } 17 | }; 18 | 19 | int main() { 20 | Node n1{1, new Node{2, nullptr}}; 21 | Node n2{3, nullptr}; 22 | Node n3{4, &n2}; 23 | } // what happen here? 24 | ``` 25 | 26 | There is a problem with the destructor, what happens when these objects go out of scope? 27 | 28 | When these objects go out of scope, they are destroyed by their destructors. All n1, n2, and n3 are stack-allocated, so all three have their destructors run. When n1's dtor runs it reclaims the rest of the list, BUT when n3's dtor runs it attempts to delete the memory at the address of n2. That's on the stack, not the heap. 29 | 30 | This is undefined behaviour, quite possible the program will crash. However, if we do not delete next, we will be leaking memory. 31 | 32 | So the class `Node` replies on an assumptiong for its proper operation: that `next` is either `nullptr` or is a valid pointer to the heap (a ptr to a valid heap allocated Node). 33 | 34 | *We need to make a rule*. 35 | 36 | An **invariant** is a statement that must hold true for all cases. (upon which Node relies) But with the way that `class Node` is implemented, we can't guarantee this invariant will hold. 37 | 38 | So, *an invariant is a statement that must hold true otherwise our program will not function correctly*. 39 | 40 | 41 | 42 | How do we make sure the statement is always true? 43 | 44 | 45 | 46 | Consider another example: 47 | 48 | An invariant for a stack is that the last item pushed is the first item popped, but we cannot guarantee this invariant if the client can rearrange the underlying data within the stack. 49 | 50 | Violating the invariant may not result in a compiling or execution error, if the client rearranges items in the stack, the stack will still return an item when `pop()` is called. However, the returned item may not be what the client was expecting. This can lead to *logical errors* (*bugs*). Logical errors are usually very difficult to debug since the program can still execute and not display any error, it may simply generate the wrong results. 51 | 52 | 53 | 54 | We introduce **encapsulation** to enforce invariants and avoid logical errors. 55 | 56 | *Encapsulation is one of the benefits of using object-oriented programming*. 57 | 58 | 59 | 60 | 61 | 62 | ## Encapsulation 63 | 64 | To enforce this invariant, we must make sure the client can't break it. We do so by introducing the idea of **encapasulation**. It is so named because we want clients to treat our object as *black boxes* or *capsules* that provide abstractions in which implementation details are hidden away, and such clients can only manipulate them in our provided methods. By doing so, we regain some control over our objects and thus our life! 65 | 66 | We implement encapsulation by setting the **access modifier** (or **visibility**) for each one of the members (fields or methods) of a class: 67 | 68 | - **private** class members can only be accessed from within the objecy (i.e., using the implicit `this` pointer of the object's methods). Any other objects (of another type) or funciton cannot direcly access private member of an object. Private fields cannot be read or modified from outside of the object's methods, and private methods cannot be called from outside of the object's methods. 69 | - **public** class members can be accedd from anywhere. Public fields can be read and modified from outside the object and public methods can be called from outside of the object. 70 | 71 | 72 | 73 | Example: 74 | 75 | ```cpp 76 | Struct Vec { 77 | Vec(int a, int b); // by default, members are **public**, accessible outside of the class 78 | private: // what follows is private, which cannot be accessed outside of struct Vec. 79 | int x, y; // client can no longer directly access this field 80 | public: // what follows is public, acccessible to all 81 | Vec operator+(const Vec &other) const; 82 | }; 83 | 84 | int main() { 85 | Vec v{1, 3}; 86 | v.x = s; // Error! x is private 87 | cout << v.x << endl; // still Error! x is private! 88 | } 89 | ``` 90 | 91 | ```cpp 92 | Vec Vec::operator+(const Vec &other) const { 93 | return {x + other.x, y + other.y}; // fine, we're inside a Vec method! 94 | } 95 | ``` 96 | 97 | We can say that *we encapsulated the implementation details of Vec*, so the client cannot access the internal state of the object directly. 98 | 99 | Note, what by default all member of structs are publice, you must specify a section as private if you want private members. 100 | 101 | In general, we want fields to be private, only some methods should be public, ... so it would be better if the default visibility is private. 102 | 103 | We can achieve exactly that by switching from the `struct` keyword to the `class` keyword. 104 | 105 | 106 | 107 | 108 | 109 | ## The `class` Keyword 110 | 111 | The **only** difference between `class` and `struct` is their default visibility: class-private, struct-public 112 | 113 | ```cpp 114 | class Vec { 115 | int x, y; // these are private by default 116 | public: 117 | Vec(int x, int y); 118 | Vec operator+(const Vec &other); 119 | }; 120 | ``` 121 | 122 | Class is the same as writing 123 | 124 | ```cpp 125 | struct Vec { 126 | Vec(int x, int y); 127 | private: 128 | int x, y; 129 | public: 130 | Vec operator+(const Vec &other) const; 131 | 132 | }; 133 | ``` 134 | 135 | 136 | 137 | 138 | 139 | Now let's fix our linked list with encapsulation. The key is to create a *wrapper class list* that has the exclusive rights to modifying the underlying Node objects. 140 | 141 | *Invariant*: next should be either nullptr or a valid pointer to a heap-allocated Node, which will be freed in the Node's destructor. 142 | 143 | We need to hide(encapsulate) the implementation details of the nodes from the clients. Allocate the Node by the list implementation, which we will guarantee that they will always be allocated in the heap. 144 | 145 | 146 | 147 | list.h 148 | 149 | ```cpp 150 | class List { 151 | Struct Node; // private nested class, only accessible within list 152 | Node *head = nullptr; // initialize the empty list 153 | public: 154 | void addToFront(int n); // adds an element to the front of the list 155 | int ith(int i); // retrieves the ith element of the list 156 | ~List(); // dtor 157 | }; 158 | ``` 159 | 160 | 161 | 162 | list.cc 163 | 164 | ```cpp 165 | // now need the details of class Node 166 | struct List::Node { // Node within the List class 167 | int data; 168 | Node *next; 169 | Node (int data, Node *next) : data{data}, next{next} {} 170 | ~Node() { delete next; } 171 | }; 172 | 173 | // List destructor: 174 | List::~List() { delete head; } // delete head ptr, and all other node will be deleted throught the node dtor 175 | 176 | void List::addToFront(int n) { 177 | head = new Node{n, head}; // the next ptr of new head will be the old head 178 | } 179 | int List::ith(int i) { 180 | Node *cur = head; 181 | for (int j = 0; j < i; ++j; cur = cur->next); // compute cur->next on the for loop 182 | return cur->data; 183 | } 184 | ``` 185 | 186 | Note that the Node is a nested class of List, so clients will never be able to use it directly. The members of the class Node don't need to be pribate as the class itself is already limited to exist within the List. 187 | 188 | Clients can never create nodes directly, they can only create initially empty lists (using list's default constructor) and add int elements using addToFront. 189 | 190 | Now that we've done all this, the client can't touch the `Node &s` in our list, so we've guaranteed that they will be either the nullptr or a ptr to a valid heap allocated Node. Great! 191 | 192 | 193 | 194 | Best practise: make members private by default and only make public those methods that need to be called from outside of the object. It is usally perferable to use `class` and then only change the necessary methods to public visibility. 195 | 196 | Remember to document the invariant using comments in your implementation! 197 | 198 | 199 | 200 | ## Accessor and Mutator Methods (Feb 17) 201 | 202 | When you use encapsulation, the clients cannot read or modify the values of the object's fields because they are all private. If you need to allow clients to read or modify those values from outside of the object, you can create **accessor** and **mutator** methods. 203 | 204 | Also known as **Getter** and **Setter**. 205 | 206 | 207 | 208 | ```cpp 209 | Class Vec { 210 | int x, y; 211 | public: 212 | int getX() const { return x; } // accessor 213 | // Node: here we return an int, which is just a copy of the value of x, thus we won't change x 214 | int getY() const { return y; } 215 | 216 | void setX(int n) { x = n; } // mutator, if we want, there can be invariant checks in here 217 | void setY(int n) { y = n; } 218 | 219 | }; 220 | ``` 221 | 222 | Naming convention: 223 | - get*Field* 224 | - set*Field* 225 | - where *Field* is the actual field name 226 | - you can replace `get` with `is` for boolean fields (i.e. is*Field*) 227 | 228 | Also by convention: accessor methods should *not* modify any state, only read and return value of a field. Therefore, accessor methods should generally be declared as `const`. 229 | 230 | 231 | ```cpp 232 | Example of invariant check: 233 | void setY(int n) { 234 | if (n == 69) return; // if n is mutating the invariant, don't let it 235 | y = n; 236 | } 237 | ``` 238 | 239 | Only create accessor methods for fields whose value of if interest to the clients, no need to create accessors for fields that store internal state inside the object. Only create mutator methods for fields whose value should be directly updated by clients, there is no need to create mutators for any internal state that should only be manipulated inside the object. 240 | 241 | 242 | 243 | The stack data structure should not have any getters and setters to protect its internal data structure, only use push() and pop() methods. 244 | 245 | ```cpp 246 | class Stack { 247 | int *elements; // private field with internal state that is not directly visible to the clients 248 | int numElements; // private field with internal state that is not directly visible to the clients 249 | public: 250 | Stack(); // constructor 251 | ~Stack(); // destructor 252 | void push(int); // adds an elementt into stack (modifies elements and numElements) 253 | int pop(); // removes and returns an element from stack (modifies elements and numElements) 254 | }; 255 | ``` 256 | 257 | 258 | 259 | 260 | 261 | It is a good application of encapsulation to enfore the invariants and aboid logical errors: 262 | 263 | ```cpp 264 | // Invariant: all grades are between 0 and 100 265 | class Student { 266 | 267 | int assns, mt, final; 268 | int enforceGradeValue(int g) const { // private method because it doesn't need to be called by clients 269 | if (g < 0) return 0; 270 | if (g > 100) return 100; 271 | return g; 272 | } 273 | 274 | public: 275 | int getAssns() const { return assns; } 276 | int setAssns(int g) { assns = enforceGradeValue(g); } 277 | int getMt() const { return mt; } 278 | int setMt(int g) { assns = enforceGradeValue(g); } 279 | int getFinal() const { return final; } 280 | void setFinal(int g) { final = enforceGradeValue(g); } 281 | 282 | }; 283 | ``` 284 | 285 | The method `enforceGradeValue` is private because it is a good practise to hide the internal implementation details and only disply to the client what they need to know to use the class. This makes the public interface smaller and easier for client to understand. 286 | 287 | Hiding unnecessary details from the client is also part of the encapsulation, even if they would not break an invariant if exposed. 288 | 289 | 290 | 291 | ## Friends! 292 | 293 | Sometimes, it may be useful to give some class privileged access to another class's private members. 294 | 295 | Recall our linked list: 296 | 297 | ```cpp 298 | // lish.h 299 | Class List { // default visibility private cuz of Class keyword 300 | struct Node; // private nested Class, only accessible within List 301 | Node &head; 302 | public: 303 | void addToFront(int n); 304 | int ith(int i); 305 | ~List(); 306 | }; 307 | ``` 308 | 309 | 310 | 311 | Since only the class List can manipulate Node Objects, we can guarantee our invariant that each next ptr points at either nullptr or a valid heap-allocated Node. 312 | 313 | Now, the **problem** is, the client can't tranverse the list from Node to Node as we would with a linked list. 314 | 315 | Repeatedly calling **i**th to access all of the list items would cause **O(n^2^)** time! Not efficient! 316 | 317 | One possible solutions is to make Node a public nested class so that clients can retrieve nodes and follow the pointer to the next node in the list, but make the Node constructor private, so the clients can never create nodes. BUT if the constructor is private, List won't be able to create Nodes either. 318 | 319 | 320 | 321 | We solve this by giving class List privileged access to Node by making it a **friend**. 322 | 323 | Solution: Power of friendship! :rainbow: (Rob insists us to draw this rainbow for "friendship") 324 | 325 | ```cpp 326 | class List { 327 | public: 328 | class Node { // public nested class 329 | int data; 330 | Node *next; 331 | Node(int data, Node *next); // constructor is now private! 332 | public: 333 | ~Node(); 334 | int getData() const; 335 | Node *getNext() const; 336 | friend class List; // List has access to all members of Node 337 | } 338 | private: 339 | Noed *head = nullptr; // private internal state 340 | public: 341 | void addToFront(int n); // adds an element to the front of the list 342 | int ith(int i) const; // retrieves the element in the ith position of the list 343 | Node *getNodeAt(int i) const; // retrieves the Node in the ith position of the list 344 | ~List(); 345 | }; 346 | ``` 347 | 348 | 349 | 350 | Now, clients can use List's method getNodeAt to retrieve a pointer to the Node in any position on the list. Node's getData method can be used to retrieve the value of the element and getNext can be used to traverse to the next node. 351 | 352 | Note the the clients cannot directly access the internal state of Node because the fields are private, but the class List can! List can access the private fields of Node and create new Node (call Node's private constructor) because we declared List as a friend class of Node. 353 | 354 | 355 | 356 | **NOTE**: in general, you should give your classes as *few* friends as possible. Friendships *weaken* encapsulation, and thus should be used only if really necessary. 357 | 358 | 359 | 360 | We also partially broke encapsulation because we exposed the implementation details of Nodes to clients. Ideally, clients should be able to traverse a list in the most efficient way possible without knowing what is the internal structure of the list (i.e., if it uses an internal array, a linked list, a tree, etc. should not matter to clients). 361 | 362 | We do this using the **Iterator Design Pattern**. 363 | 364 | 365 | 366 | ### Friend Functions 367 | 368 | Example: the Vec alss, the x and y fields are private and have no accessor methods: 369 | 370 | ```cpp 371 | class Vec { 372 | int x, y; 373 | }; 374 | ``` 375 | 376 | 377 | 378 | What if we want to implement `operator<<` and `operator>>` to print the values of x and y? We need access to x and y, and `operator<<` and `operator>>` cannot be a member function. 379 | 380 | If we add getter and setters just for these functions, everyone can access them, but we don't necessarily want that ---- Yet again, friendship! :rainbow: We can make `operator<<` a **friend function**. 381 | 382 | 383 | 384 | ```cpp 385 | // vec.h 386 | class Vec { 387 | int x, y; 388 | friend std::ostream &operator<<(std::ostream &out, const Vec &v); 389 | // as soon as we say it is a friend, it is no longer a member function 390 | }; 391 | 392 | // vec.cc 393 | std::ostream &operator<<(std::ostream &out, const Vec &v) { 394 | return out << v.x << ' ' << v.y; 395 | } 396 | ``` 397 | 398 | 399 | 400 | Friend declaration doesn't matter if you declare it in the public or private section. Usually you put friends in the end of the class, by convention. 401 | 402 | Friends functions also weaken encapsulation and should be used only if *really* necessary. 403 | 404 | 405 | 406 | 407 | 408 | # 5.2 Design Patterns and Iterators 409 | 410 | 411 | 412 | ## Design Patterns Introduction 413 | 414 | Certain programming senarios or problems are common and arise often, traversing the linked list is one such problem. We as programmers keep track of good solutions to these problems so we can reuse them. 415 | 416 | 417 | 418 | This is the essence of **design patterns**. If you have this situation, then this programming solution (method) may solve it. 419 | 420 | A **design** pattern is a codified solution to a common software problem. It specifically deals with a problem in object oriented software development, and thus focuses on the involved classes and their relationships. 421 | 422 | A design pattern has 4 key elements: 423 | 424 | 1. a memorable name that describes the problem or solution 425 | 2. a description of the problem to solve 426 | 3. the general form of the solution, and 427 | 4. the consequences (results and trade-offs) of using the pattern 428 | 429 | 430 | 431 | The guiding principle behing all of the design patterns is: encapsulate change on design decisions. i.e. program to the *interface*, not the *implementation*. The class relationships thus rely heavily upon abstract base classes. 432 | 433 | 434 | 435 | ## Iterator 436 | 437 | The problem of encapsulated linked list: we can't traverse the list from node to node, repeatedly calling `ith` to access all of the list items would cost O(n^2^) time, but we can't expose the nodes or we lose encapsulation. 438 | 439 | 440 | 441 | Solution: how do I allow client to iterate my data in a hidden structure without revealing the structure. 442 | 443 | **Iterator Problem**: we create a class that manages access to Nodes (some access, not all). 444 | 445 | This iterator class is effectively an *abstraction* of a pointer. (It's going to behave like a ptr, you can use it as a ptr, but not as always, you can't change anything about this ptr). This will let us walk through the list, without exposing actual pointers. 446 | 447 | 448 | 449 | **General format** of an iterator: 450 | 451 | ```cpp 452 | class MyclassIterator { 453 | // some private field to keep track of where the iterator is currently pointing to 454 | explicit MyClassIterator (/* a parameter to initialize it */); // private constructor 455 | public: 456 | MyClass &operator*(); // access the item the iterator is currently pointing to (return type can be different if needed) 457 | MyClassIterator &operator++(); // advances the iterator to the next item and returns the iterator 458 | bool operator==(const MyClassIterator &other) const; // returns true if both Iterators are equal (point to the same item) 459 | bool operator!=(const MyClassIterator &other) const; //returns true if both Iterators are different (do not point to the same item) 460 | 461 | }; 462 | ``` 463 | 464 | 465 | 466 | Linked List with an Iterator: 467 | 468 | ```cpp 469 | Class List { 470 | struct Node; 471 | Node &head; 472 | public: // add a public nested class, class Iterator 473 | Class Iterator { // List::Iterator 474 | Node &p; 475 | public: 476 | explicit Iterator(Node &p) : p{p} {} 477 | // we want to access the data of the ptr, the dereference operator 478 | int &operator*() { return p->data; } 479 | // overload the prefix operator ++, returns the thing that is incremented 480 | Iterator &operator++() { // prefer the prefix operator in loops (i.e. ++i) 481 | p = p->next; 482 | return *this; 483 | } 484 | // to overload the postfix operator: 485 | // operator++(int)() 486 | bool operator==(const Iterator &other) const { 487 | // an Iterator is equal to another if their pointers are equal 488 | return p == other.p; 489 | } 490 | bool operator!=(const Iterator &other) const { 491 | // the reverse of the equals operator 492 | return !(*this == other); // use the above defined == 493 | } 494 | }; // end of List::Iterator 495 | 496 | // we want to know the end iterator (a token that signals we are done) 497 | Iterator end() { return Iterator{nullptr}; } // linked list ends with a nullptr 498 | // we also need to know what's the start 499 | Iterator begin() { return Iterator{head}; } 500 | // .. Any other list functions (addToFront, ith, etc.) 501 | 502 | } 503 | ``` 504 | 505 | Now, the client can walk our list in O(n) (linear time!) 506 | 507 | ```cpp 508 | int main() { 509 | List lst; 510 | lst.addToFront(1); 511 | lst.addToFront(2); 512 | lst.addToFront(3); 513 | // 3->2->1 514 | for (List::Iterator it = lst.begin(); it != lst.end(); ++it) { 515 | cout << *it << endl; // prints 3 2 1 516 | } 517 | } 518 | ``` 519 | 520 | 521 | 522 | Writing out `List::Iterator` is a mouthful.. Well, `it` is being assigned to the return value of a function. 523 | 524 | - the compiler *knows* what type that function returns, then we should be able to ask the compiler to figure out the type for us. We can do so with the keyword `auto`, by taking advantage of **automatic type deduction**. 525 | 526 | - A definition like `auto x = y;` defines x to have the same type as y. The main advantage is that we don't need to write down the exact type of x, which may be complex. 527 | 528 | - ```cpp 529 | for (auto it = lst.begin(); lt != lst.end(); ++it) 530 | ``` 531 | 532 | - use auto sparingly, it could make code unreadable as the reader may not know the types. But, the iterator pattern is so pervasive to c++ it's quite clear what we mean here. 533 | 534 | - It's **so pervasive** in C++ that there's a special form of for loops for using iterators: **Range-based for loops** 535 | 536 | ```cpp 537 | for (auto n : lst) { // Range based for loop, works on any container that implements the iterator pattern! 538 | cout << n << endl; 539 | } // Note: n is already the value of derefering the iterator, auto is an int here 540 | ``` 541 | 542 | n is an `int`, we are declaring an `int` variable `n`, so: 543 | 544 | ```cpp 545 | for (auto n : lst) { 546 | n = n + 1; // list doesn't change, only mutating variable n, which is a copy of each element in the list 547 | } 548 | ``` 549 | 550 | If we want to mutate the elements in list, we declare n as a int reference 551 | 552 | ```cpp 553 | for (auto &n : lst) { 554 | ++n; // now mutates the list, n refers to the same thing as the reference returned by the deference iterator 555 | } 556 | ``` 557 | 558 | Range based for-loops are available for any type T with the following properties (T is our container type) 559 | 560 | - T must provide `begin()` and `end()` methods that take no parameters and return some type I (I is our iterator type) 561 | - Type I must define inequality `operator!=` between two type Is, increment prefix operator `++`, and unary operator `*` 562 | - any classes T and I that have the above behaviour mean you can write a range based for loop over objects of type T. 563 | 564 | 565 | 566 | ```cpp 567 | Class IntArray { 568 | int *theArray; 569 | int size; 570 | int capacity; 571 | public: 572 | int *begin() { return theArray; } 573 | int *end() { return theArray + size; } // ptr arithematic here 574 | }; 575 | // an int* type has its operator!=, operator++, operator* already defined. 576 | ``` 577 | 578 | 579 | 580 | A minimal implementation of the Iterator design pattern requires at least five operators 581 | 582 | - `begin ` in the base class 583 | - `end` in the base class 584 | - `operator++` in the Iterator class 585 | - `operator*` in the Iterator class 586 | - `operator!=` in the Iterator class 587 | 588 | 589 | 590 | 591 | ### Friend for Iterator 592 | 593 | One critique of the Iterator solution is, consider: 594 | 595 | ```cpp 596 | // main.cc 597 | auto it = List::Iterator(nullptr); 598 | ``` 599 | 600 | This violates encapsulation - the client should only create iterators begin and and. 601 | 602 | We could make the ctor private, but then List can't construct iterators either, so we can't implement begin and end. 603 | 604 | 605 | 606 | We give List priviliged access to class iterator by declaring it a a friend. 607 | 608 | ```cpp 609 | Class List { 610 | public: 611 | Class Iterator { 612 | Node *p; 613 | explicit Iterator(Node &p) : p{p} {} // private constructor 614 | pubilc: 615 | friend Class List; // :), list can access all member of Iterator 616 | }; 617 | }; 618 | ``` 619 | 620 | Now, List can access private members of Class Iterator, so, begin and end can now construct iterators, but the clients can't. Iterators cannot access private members of a list! Good programming in C++: Have as few friends as possible! 621 | 622 | But be careful: Friends weaken encapsulation. 623 | 624 | -------------------------------------------------------------------------------- /7. The Standard Template Library.md: -------------------------------------------------------------------------------- 1 | # 7. The Standard Template Library (STL) 2 | 3 | 4 | 5 | 6 | 7 | ## Templates (March 10) 8 | 9 | 10 | 11 | Template programming allows us to create parameterized classes (**templates**) that are specialized to actual code when we need to use them. The advantage is that we can use the template code to generate many concrete classes without having to duplicate code. 12 | 13 | 14 | 15 | **Note**: templates are a **large** and **complex** topic. 16 | 17 | 18 | 19 | Example: if we want to implement a class List for int data and another for float data, we could copy and paste the code and just change the type of the private fields within the Nodes 20 | 21 | The class List: 22 | 23 | ```cpp 24 | class List { 25 | struct Node; 26 | Node *theList; 27 | ... 28 | }; 29 | 30 | struct List::Node { 31 | int data; 32 | Node *next; 33 | }; 34 | ``` 35 | 36 | 37 | 38 | ```cpp 39 | class IntList { 40 | struct Node { 41 | int data; 42 | Node *next; 43 | ... 44 | }; 45 | Node *theList; 46 | public: 47 | ... 48 | }; 49 | 50 | class FloatList { 51 | struct Node { 52 | float data; 53 | Node *next; 54 | ... 55 | }; 56 | Node *theList; 57 | public: 58 | ... 59 | }; 60 | ``` 61 | 62 | Copying and Pasting is never a good idea. What if we want to store something other than int? Do we need to rewrite a whole new class? 63 | 64 | 65 | 66 | To avoid this code duplication, we can create a List **template** with a parameter that corresponds to the type of data stored in the list. 67 | 68 | A **template** class is a class that's parameterized by a type. 69 | 70 | 71 | 72 | ```cpp 73 | template class List { 74 | struct Node { 75 | T data; 76 | Node *next; 77 | ... // big 5 78 | }; // List::Node 79 | Node *theList; 80 | public: 81 | class Iterator { 82 | Node *p; 83 | explicit Iterator(Node *p) : p{p} {} 84 | public: 85 | T &operator*() { return p->data; } 86 | Iterator &operator++(){...} 87 | ... 88 | friend class List; 89 | }; // List::Iterator 90 | void addToFront(const T &n) {...} 91 | }; 92 | ``` 93 | 94 | Now, out List class can store any type of data. To create a new List object, we need to specify the value of the parameter T, i.e., the type of data we want to store. 95 | 96 | When the program is executed, each instance of T in the code of the List will be replaced with the actual type. 97 | 98 | ```cpp 99 | List ls; // string is the value of the template parameter T, 104 | // so each T in the List's code will be replaced with string 105 | ls.addToFront("hello"); 106 | ``` 107 | 108 | 109 | 110 | Client code: 111 | 112 | ```cpp 113 | List l1; 114 | List> l2; 115 | l1.addToFront(3); 116 | l2.addToFront(l1); 117 | 118 | for (List class Stack { 135 | int size; 136 | int cap; 137 | T *contents; 138 | public: 139 | stack() {...} 140 | void push(T x) {...} 141 | T top() const { return contents[size-1]; } 142 | void pop() {...} 143 | }; 144 | ``` 145 | 146 | 147 | 148 | *How do templates work?* Compiler specializes the templates into actual code as a source-level transformation (at the source code level before compilation) and then compiles the resulting code as usual. 149 | 150 | 151 | 152 | **Node that because of the way that templates work, the implementation of the template needs to go in the .h file instead of the .cc file as usual.** 153 | 154 | What types can T be? Use duck-typing to determine. 155 | 156 | **Duck-typing**: if it looks like a duck, walks like a duck, and quacks like a duck, then it is a duck. 157 | 158 | So the valid types of T are any types that support the ways in which you use T. 159 | 160 | Consider: 161 | 162 | ```cpp 163 | template class Foo { 164 | T x; 165 | public: 166 | Foo operator+(const Foo &other) { return Foo{x + other.x}} 167 | } 168 | ``` 169 | 170 | 171 | 172 | Additionally, note that in the declaration of the template parameters, `typename` is the required keyword, but T is only a common name for type by convention. You can use something other than T if you prefer. When the template has more than one parameter, it is common to use an upper-case character related to the meaning of the type. 173 | 174 | For example, you could use K as the name of the type for a key, V for a value, I for an index, etc. These are just conventions. 175 | 176 | ```cpp 177 | template class Dictionary { 178 | K key; 179 | V value; 180 | ... 181 | } 182 | Dictionary d; // Each K in Dictionary will be replaced with string and each V will be replaced with Student 183 | ``` 184 | 185 | 186 | 187 | ## Standard Template Library (STL) 188 | 189 | The **Standard Template Library (STL)** is a large collection of useful templates that already exist in C++. 190 | 191 | It contains collection classes such as lists, vectors, maps, deques, etc, iterators to traverse the elements in those collections and generic functions to operate on them, such as initialization, sorting, searching, and transformation of the elements. 192 | 193 | The template classes in the STL are explicitly structures not to allow inheritance. You can't derive from them to extend their behaviour because their methods are not virtual (but you could extend their behaviour using **Decorators**). 194 | 195 | 196 | 197 | ## STL `std::vector` 198 | 199 | The class `std::vector` is a generic (template) implementation of dynamic-length arrays. 200 | 201 | Example: use vector to create a dynamic-length array of integers 202 | 203 | ```cpp 204 | #include 205 | using namespace std; 206 | 207 | ... 208 | vector v; // because it is a template, we need to specify the type of data to store, which is int in this example 209 | v.emplace_back(6); // {6} 210 | v.emplace_back(7); // {6, 7} 211 | 212 | vector u{4, 5}; // {4, 5} 213 | ``` 214 | 215 | 216 | 217 | The methods `emplace_back()` or `push_back()` can be used to add elements to the vector. The difference is `emplace_back()` creates a new object by using the class constructor before adding it to the array, whereas `push_back()` copies or moves the content from an existing object into the array. 218 | 219 | **Note**: you don't pass the actual object to `emplace_back`, you pass the *arguments to be used to construct the object*, and `emplace_back` calls the constructor for you. This is extremely useful when you want to create an actual object "in place" in the container rather than first creating the object and then moving (or copying) it into the container. 220 | 221 | 222 | 223 | Example: create a vector of Vec objects using `emplace_back`: 224 | 225 | ```cpp 226 | #include 227 | #include 228 | 229 | struct Vec { 230 | int x, y; 231 | Vec(int x, int y) : x{x}, y{y} {} 232 | }; 233 | 234 | int main() { 235 | std::vector v; 236 | for (int i = 0; i < 5; i++) { 237 | v.emplace_back(i, i+1); // invokes Vec ctor 238 | } 239 | for (const auto & i : v) { 240 | std::cout << "(" << i.x << ", " << i,y << ")" << std::endl; 241 | } 242 | } 243 | ``` 244 | 245 | 246 | 247 | ### Looping over vectors 248 | 249 | You can use a for loop to visit a vector's contents by indexing: 250 | 251 | ```cpp 252 | for (std::size_t i = 0; i < v.size(); ++i) { 253 | cout << v[i] << endl; 254 | } 255 | 256 | for (int i = 0; i < v.size(); ++i) { 257 | cout << v[i] << endl; 258 | } 259 | ``` 260 | 261 | 262 | 263 | Vectors also support the **iterator** abstraction: 264 | 265 | ```cpp 266 | // iterator here is in lower case 267 | for (vector::iterator it = v.begin(); it != v.end(); ++it) { 268 | cout << *it << endl; 269 | } 270 | 271 | // or use auto 272 | for (auto n : v) { 273 | cout << n << endl; 274 | } 275 | ``` 276 | 277 | 278 | 279 | To iterate in **reverse**: 280 | 281 | ```cpp 282 | for (vector::reverse_iterator it = v.rbegin(); it != v.rend(); ++it) { 283 | cout << *it << endl; 284 | } 285 | 286 | for (auto it = v.rbegin(); it != v.rend(); ++it) { 287 | cout << *it << endl; 288 | } 289 | ``` 290 | 291 | 292 | 293 | To **remove** the last element: 294 | 295 | ```cpp 296 | v.pop_back(); // removes last element of vector 297 | *(--v.end()); // last element 298 | *(v.end() - 2); // second last element 299 | ``` 300 | 301 | 302 | 303 | Other vector operations are based on iterators. 304 | 305 | The **`erase`** method which removes element from a vector, work with iterators: 306 | 307 | ```cpp 308 | auto it = v.erase(v.begin()); // erase item 0, returns iterator to first item after the erase 309 | it = v.erase(v.begin() + 3); // erase item 3 (4th item) 310 | it = v.erase(it); // erases item pointed to by it 311 | it = v.erase(v.end() - 1); // erase last item 312 | 313 | ``` 314 | 315 | 316 | 317 | 318 | 319 | **Use Vectors instead of dynamic-length arrays** 320 | 321 | Vectors are guaranteed to be implemented internally as arrays. Use them whenever you need a dynamic-length array (i.e. avoid using the array versions of new and delete, as using these operators usually indicates an opportunity to use a vector instead). 322 | 323 | If you want a non-dynamic (static) length array, std::array exists, but you could just use a vector and not change its size. 324 | 325 | 326 | 327 | 328 | 329 | Consider: 330 | 331 | ```cpp 332 | vector v{0, 1, 2}; 333 | auto it = v.begin(); 334 | for (int i = 0; i < 1000; ++i) { 335 | v.emplace_back(i); 336 | } 337 | cout << *it << endl; // undefined behaviour, iterator invalidation! 338 | ``` 339 | 340 | **Accessing elements**: 341 | 342 | ```cpp 343 | v[i]; // returns ith element of v (completely unchecked), just like an array index 344 | // v[i] -- if you go out of bounds, the behaviour is undefined 345 | 346 | v.at(i); // same as v[i] but checked. It makes sure you don't go out of bound, but what does it do if it is out of bound? 347 | 348 | 349 | ``` 350 | 351 | v.at(i) throws an out_of_range exception 352 | 353 | Because of the additional checking, v[i] is more efficient than v.at(i). Thus, it may be a good idea to use v.at(i) at the initial stages of development and replace it with v[i] for production code after testing the program thoroughly. 354 | 355 | 356 | 357 | 358 | 359 | **Problem**: What should happen when using v.at(i) when i is out of bound? 360 | 361 | **C Solution**: 362 | 363 | - some functions that might fail return a status code 364 | - or set the global variable error 365 | - leads to awkward programming 366 | - encourages programmers to ignore error checks 367 | 368 | 369 | 370 | **C++ Solution**: 371 | 372 | - when an error conditions arises, the function raises an exception 373 | - What happens? by default, execution stops 374 | 375 | 376 | 377 | 378 | 379 | ## STL `std::map` 380 | 381 | The class `std::map` can be used to implement dictionaries, in which unique keys are mapped to values. It is a generic (template) class, so we can define any type for the keys and values. (Well, the key must be of a type that supports `operator<`, which compare and sort the keys) 382 | 383 | 384 | 385 | Example: map of string keys to int values (note that `map` is in the `` library and the `std` namespace) 386 | 387 | ```cpp 388 | #include 389 | using namespace std; 390 | ... 391 | map m; // string is the key type, and int is the value type 392 | // Setting the values for the keys "abc" and "def" 393 | m["abc"] = 1; 394 | m["def"] = 4; 395 | // Reading the values associated with each key 396 | cout << m["abc"] << endl; // 1 397 | cout << m["ghi"] << endl; // 0 398 | // key "ghi" didn't exist in our map, but the index operator creates keys if they don't exist... but we never set the value, so what is it? 399 | // the value gets default initialized, integer get 0 400 | ``` 401 | 402 | Here, "ghi" is not a key defined in m, so it is not found. It a key is not found when trying to read it, it is inserted and the value if default-constructed (for an int, the default value is zero). 403 | 404 | 405 | 406 | The `erase` method can be used to delete a key and its associated value: 407 | 408 | ```cpp 409 | m.erase("abc"); 410 | ``` 411 | 412 | 413 | 414 | The `count` method returns one if a key is found in the map, or zero otherwise: 415 | 416 | ```cpp 417 | if (m.count("def")) ... // 0 = not found, 1 = found 418 | ``` 419 | 420 | 421 | 422 | **Iterating** over a map: 423 | 424 | Iterating over a map happens in sorted key order: 425 | 426 | ```cpp 427 | for (auto &p : m) { 428 | cout << p.first << ' ' << p.second << endl; 429 | // Note: first and second are fields, not methods 430 | } 431 | ``` 432 | 433 | p's type here is `std::pair&` (pairs are defined in ``). 434 | 435 | 436 | 437 | -------------------------------------------------------------------------------- /8. Error Handling with Exceptions.md: -------------------------------------------------------------------------------- 1 | # 8. Error Handling with Exceptions 2 | 3 | ## Introduction to Exceptions (March 10) 4 | 5 | RECALL our vectors code: 6 | 7 | ```cpp 8 | v[i]; // unchecked 9 | v.at(i); // checked, but what should happen when out of bound? 10 | ``` 11 | 12 | Vectors code can detect the error, but it doesn't know about it. Client can respond but can't detect the error. Error recovery is a non-local problem. 13 | 14 | 15 | 16 | **Problem**: What should happen when using v.at(i) when i is out of bound? 17 | 18 | **C Solution**: 19 | 20 | - some functions that might fail return a status code or set the global variable error 21 | - leads to awkward programming, the awkward programing encourages programmers to ignore error checks 22 | 23 | 24 | 25 | **C++ Solution**: 26 | 27 | - when an error conditions arises, the function **raises** (or **throws**) an **exception** 28 | - Then what happens? By default, execution stops (if nothing else is done). 29 | - we can write **handlers** to **catch** exceptions and deal with them. 30 | 31 | 32 | 33 | vector::at rises the exception std::out_of_range when it fails. We can handle it like so: 34 | 35 | ```cpp 36 | #include 37 | ... 38 | try { 39 | cout << v.at(1000) << endl; // statements that may raise an exception 40 | } catch (out_of_range r) { // r is the exception and std::out_of_range is the type 41 | // specify what to do if it fails 42 | cerr << "Range Error" << r.what() << endl; // r.what() is a description 43 | } 44 | ``` 45 | 46 | 47 | 48 | 49 | 50 | ### Throwing and Handling Exception 51 | 52 | From course notes Spring 2021. 53 | 54 | Consider the Student class, you are writing the helper function `checkGrade`, which should report an error if the submitted grade is lower than zero or higher than 100: 55 | 56 | ```cpp 57 | // helper function 58 | int checkGrade( int grade ) { 59 | if (grade >= 0 && grade <= 100) { 60 | return grade; 61 | } else { 62 | // How should we report this error? 63 | } 64 | } 65 | 66 | 67 | class Student { 68 | const int id; 69 | int assns, mt, final; 70 | public: 71 | Student(const int id, int assns = 0, int mt = 0, int final = 0) 72 | : id{id}, assns{assns}, mt{checkGrade(mt)}, final{checkGrade(final)} {} 73 | float grade() const { 74 | return assns * 0.4 + mt * 0.2 + final * 0.4; 75 | } 76 | }; 77 | 78 | ``` 79 | 80 | 81 | 82 | ```cpp 83 | // main.cc 84 | 85 | int main() { 86 | // How would we detect here if these grades are valid? 87 | Student s{7899, -10, 50, 150}; 88 | cout << "s.grade() = " << s.grade() << endl; 89 | } 90 | ``` 91 | 92 | What should happen when the function checkGrade detects an invalid grade? 93 | 94 | 95 | 96 | The function **raises**/**throws** an **exception**, and we write **handlers** to **catch** exceptions and deal with them. 97 | 98 | In C++, we can throw anything, but the usual practice is to define specific exception classes. This makes exception handling easier because the client can catch specific exception classes. 99 | 100 | ```cpp 101 | class InvalidGrade { 102 | // we will add fields later 103 | }; 104 | 105 | // helper function 106 | int checkGrade( int grade ) { 107 | if (grade >= 0 && grade <= 100) { 108 | return grade; 109 | } else { 110 | throw InvalidGrade{}; 111 | } 112 | } 113 | ``` 114 | 115 | To handle exceptions, we use a *try-catch* block. 116 | 117 | ```cpp 118 | // in main.cc 119 | int main() { 120 | try { 121 | Student s{7899, -10, 50, 150}; 122 | cout << "s.grade() = " << s.grade() << endl; 123 | } catch (InvalidGrade) { 124 | cout << "Invalid grade." << endl; 125 | } 126 | } 127 | ``` 128 | 129 | All the statements that go within the `try{ }` block are *protected*, meaning that if an exception is raised while executing any of them, the execution will move to catch blocks. 130 | 131 | In a catch block, we specify the type of exception that we want to handle. In this case, objects of the InvalidGrade class. 132 | 133 | If an exception of this type is raised, the statements within the `catch{ }` block are executed. 134 | 135 | After that, the program's execution continues on the next line after the catch block. If an exception of any other type is raised, the catch block won't be executed and the program will terminate immediately just as if we did not have any try-catch. 136 | 137 | Thus, *if you don't have a catch block that matches the type of the raised exception, it's just like not having any catch block at all*. 138 | 139 | 140 | 141 | It would be better if we could pass additional information in the exception, which could be displayed in the error message. As the exception is just an object, we can add fields to the class, which we can populate with information about the error before throwing the exception. 142 | 143 | ```cpp 144 | class InvalidGrade { 145 | int grade; 146 | public: 147 | InvalidGrade(grade) : grade{grade} {} 148 | int getGrade() const { return grade; } 149 | }; 150 | 151 | // helper function 152 | int checkGrade( int grade ) { 153 | if (grade >= 0 && grade <= 100) { 154 | return grade; 155 | } else { 156 | throw InvalidGrade{grade}; 157 | } 158 | } 159 | ``` 160 | 161 | Update our exception handler by catching the thrown object into the variable ex, then we can read the information in the object to include it into our error message: 162 | 163 | ```cpp 164 | // in main.cc 165 | int main() { 166 | try { 167 | Student s{7899, -10, 50, 150}; 168 | cout << "s.grade() = " << s.grade() << endl; 169 | } catch (InvalidGrade ex) { 170 | cout << "Invalid grade: " << ex.getGrade() << endl; 171 | } 172 | } 173 | ``` 174 | 175 | 176 | 177 | Exceptions can be any complex object, so there is no limit on the amount of information that you can include on the object. For performance optimization, it is recommended to pass just the necessary information. However, programmers often use the attributes of the exception to provide enough detail about it, which the program can use to recover from the exception and continue its execution, or to display an appropriate message to the user. 178 | 179 | 180 | 181 | A great benefit of programming with exceptions is that once you catch an exception, the program does not terminate. Execution continues right after the catch block. 182 | 183 | Example: in a loop 184 | 185 | ```cpp 186 | int main() { 187 | Student s; 188 | while (true) { 189 | try { 190 | // assume that we have a method to read a Student object from cin, which may throw an InvalidGrade exception 191 | cin >> s; 192 | cout << "s.grade() = " << s.grade() << endl; 193 | } catch (InvalidGrade ex) { 194 | cout << "Invalid grade: " << ex.getGrade() << endl; 195 | } 196 | // write some condition to break the while loop 197 | } 198 | } 199 | ``` 200 | 201 | 202 | 203 | ## Exceptions and the Call Chain (March 10) 204 | 205 | 206 | 207 | What happens when an exception is raised in a call chain? 208 | 209 | ```cpp 210 | void f() { 211 | throw out_of_range{"f"}; // this "f" here is the what() 212 | // raises an exception, "f" will be returned by .what() 213 | } 214 | 215 | void g() { f(); } 216 | void h() { g(); } 217 | 218 | 219 | int main() { 220 | try { 221 | h(); 222 | } catch (out_of_range r) {...} 223 | } 224 | ``` 225 | 226 | So main calls h, h calls g, and g calls f, then f throws (out_of_range). Control goes back through the call chain (unwinding the stack) until a handler is found. If a function "foo" doesn't catch an exception that was thrown by a function it called, then foo propagates that exception. If there is no matching handler in the entire call chain then the program terminates. 227 | 228 | `out_of_range` is a class. The statement `throw out_of_range{"f"}` calls out_of_range's constructor with argument `"f"` (the info for `.what()`). 229 | 230 | 231 | 232 | ```cpp 233 | void f() { 234 | vector x{1, 2, 3, 4, 5}; 235 | int *p = new int[100]; 236 | try { ... } 237 | catch (someError s) { 238 | throw someError{ ... }; 239 | } 240 | delete []p; 241 | } 242 | // memory leak with p, but not x 243 | ``` 244 | 245 | 246 | 247 | ## Partial Exception Handling 248 | 249 | A handler can do part of the recovery job (i.e. execute some corrective code, and throw another exception): 250 | 251 | ```cpp 252 | try { ... } 253 | catch (someErrorType s) { 254 | throw someOtherError{...}; 255 | } 256 | ``` 257 | 258 | we can do `throw;` instead of `throw s;` to **rethrow** the exact same error received (maintaining its static type) 259 | 260 | `throw s;` throws the copy which may be sliced even if we catch by reference 261 | 262 | ```cpp 263 | try {...} 264 | catch (SomeErrorType s) { 265 | ... 266 | throw; 267 | } 268 | ``` 269 | 270 | This is useful when a function needs to do some cleanup, but it won't be able to completely handle the error. 271 | 272 | For example, if a function allocated dynamic memory, a **partial exception** handler can free it before rethrowing the original exception. Therefore, the function avoids a memory leak but lets someone else handle the exception. 273 | 274 | ```cpp 275 | void f() { 276 | throw out_of_range{"f"}; 277 | } 278 | void g() { 279 | f(); 280 | } 281 | 282 | //// modify this ////////////// 283 | void h() { 284 | cout << "Start h" <, endl; 285 | try { 286 | g(); 287 | } catch (out_of_range) { 288 | cer << "Range error in h()" << endl; 289 | throw; 290 | } 291 | cout << "Finish h" << endl; 292 | } 293 | 294 | ///////////////////////////// 295 | 296 | int main() { 297 | try { 298 | h(); 299 | } 300 | catch (out_of_range) { 301 | cerr << "Range error in main()" << endl; 302 | } 303 | } 304 | 305 | ``` 306 | 307 | 308 | 309 | Generally define your own meaningful error classes to throw exceptions 310 | 311 | ```cpp 312 | class BadInput { 313 | int n; 314 | } 315 | ``` 316 | 317 | 318 | 319 | Catching exceptions by reference prevents slicing when catching a base class, which in theory could have virtual methods. So if we slice and force a base class object we might lose info specialized behaviour. Catching by reference is usually the *right* thing to do. 320 | 321 | ***“Throw by value, catch by reference."*** 322 | 323 | 324 | 325 | **Warning**: NEVER let a destructor throw! By default, a program will terminate immediately because the destructor is implicitly noexcept. If you turn this off and allow destructor to throw, the program will terminate if there are two unhandled exceptions - which might occur during unwinding of a stack from another exception. 326 | 327 | If the destructor is being executed during stack unwinding while dealing with another exception, you now have *two* active, unhandled exceptions and the program *will* abort immediately (by calling `std::terminate`). 328 | 329 | 330 | 331 | 332 | 333 | ## March 15 334 | 335 | RECALL: 336 | 337 | ```cpp 338 | try {...} 339 | catch (someErrorType s) { 340 | // do your error recovery 341 | throw someOtherError{}; // there is still an error that need to be dealt with 342 | } 343 | ``` 344 | 345 | OR, you can throw the same exception 346 | 347 | ```cpp 348 | try {...} 349 | catch (someErrorType s) { 350 | throw; // just say throw, and it is the same exception 351 | } 352 | ``` 353 | 354 | 355 | 356 | ```cpp 357 | // exn.cc 358 | #include 359 | using namespace std; 360 | 361 | class SomeErrorType {}; 362 | class SpecialErrorType : public SomeErrorType {}; 363 | 364 | void foo() { 365 | throw SpecialErrorType{}; 366 | } 367 | 368 | void bar() { 369 | try { 370 | foo(); // throws SpecialErrorType, which is a SomeErrorType 371 | } catch (SomeErrorType s) { 372 | // code for bar's portion of error recovery 373 | throw s; 374 | } 375 | } 376 | 377 | int main() { 378 | try { 379 | bar(); 380 | } catch (SpecialErrorType s) { 381 | cout << "Caught Special Error Type" << endl; 382 | } catch (...) { // ... is the syntax for "catch any exception" 383 | cout << "Caught something" << endl; 384 | } 385 | } 386 | ``` 387 | 388 | `catch (...)` catches any exception 389 | 390 | Here we say `throw s;`, and the output is 391 | 392 | ``` 393 | Caught something 394 | ``` 395 | 396 | If we say `throw;`, we get the output 397 | 398 | ``` 399 | Caught Special Error Type 400 | ``` 401 | 402 | 403 | 404 | Why do we say `throw;` rather than `throw s;`? 405 | 406 | Because the exception might actually belong to a subclass of the error type we caught (in this case, SpecialErrorType and SomeErrorType). 407 | 408 | `throw;` rethrows the exact error received (even maintaining its static type) whereas `throw s;` throws the copy which may be sliced. Even if we catch by reference (no slicing), `throw s;`, the static type is whatever `s` is (e.g. SomeErrorType & vs. SpecialErrorType) 409 | 410 | 411 | 412 | A handler can also be a ***catch-all***. 413 | 414 | ```cpp 415 | try {...} 416 | catch (...) { // <= literally mean ',,,' 417 | // ,,, <= figuratively mean ... 418 | } 419 | ``` 420 | 421 | 422 | 423 | You can throw anything you want, doesn't have to be an object (preferably is a meaningful error object). 424 | 425 | Let's write a very silly program: 426 | 427 | ```cpp 428 | #include 429 | using namespace std; 430 | 431 | void fact(int n) { 432 | if (n == 1) { 433 | throw 1; 434 | } 435 | try { fact(n - 1); } 436 | catch (int x) { 437 | throw x * n; 438 | } 439 | } 440 | 441 | int main() { 442 | try { 443 | fact(5); 444 | } catch (int n) { 445 | cout << "fact(5) = " << n << endl; 446 | } 447 | } 448 | 449 | ``` 450 | 451 | 452 | 453 | Write a Fibonacci sequence: 454 | 455 | ```cpp 456 | #include 457 | using namespace std; 458 | 459 | void fib(int n) { 460 | if (n == 0 || n == 1) throw 1; 461 | try { 462 | fib(n - 1); 463 | } catch (int fnm1) { 464 | try { 465 | fib(n-2); 466 | } catch (int fnm2) { 467 | throw fnm1 + fnm2; 468 | } 469 | } 470 | } 471 | 472 | int main() { 473 | try { 474 | fib(30); 475 | } catch (int fib) { 476 | cout << fib << endl; 477 | } 478 | } 479 | ``` 480 | 481 | Do not throw exceptions with control flows! What we did is silly. 482 | 483 | 484 | 485 | Generally, define your own meaningful error classes to throw as exceptions. 486 | 487 | ```cpp 488 | class BadInput {}; 489 | 490 | try { 491 | int n; 492 | if (!cin >> n) throw BadInput{}; 493 | } catch (BadInput &) { 494 | cerr << "Input not well formed" << endl; 495 | } 496 | ``` 497 | 498 | 499 | 500 | Catching exceptions by reference prevents **slicing** when catching a base class. Which in theory, could also have virtual methods. So if we slice and force into a base class object, we might lose information/specialized behaviour. Catching by reference is usually the correct thing to do. 501 | 502 | The maxim in C++ is: ***"Throw by value, catch by reference"*** 503 | 504 | 505 | 506 | **WARNING**: **NEVER** let a destructor throw an exception! By default, a program will terminate immediately because the dtor is implicitly *noexcept*. If you turn this off and allow your dtor to throw, the program will immediately terminate if there are two unhandled exceptions. If a destructor throws, this might occur during the unwinding of the stack from another exception. 507 | 508 | -------------------------------------------------------------------------------- /9. Design Patterns.md: -------------------------------------------------------------------------------- 1 | # 9. Design Pattern 2 | 3 | 4 | 5 | Guiding principle: *program to interfaces, not implementations*. 6 | 7 | Abstract Base classes define the interface, work with base class pointers and call their (usually virtual) methods. Concrete subclasses can then be swapped in and out - provides abstraction over a variety of behaviour. 8 | 9 | Example: Iterator Pattern 10 | 11 | ```cpp 12 | class AbstractIterator { // could make a template 13 | public: 14 | virtual int &operator*() const = 0; // could be a template type 15 | virtual AbstractIterator &operator++() = 0; 16 | bool operator!=(const AbstractIterator &other) const = 0; 17 | virtual ~AbstractIterator(); 18 | }; 19 | ``` 20 | 21 | 22 | 23 | ```cpp 24 | class List { 25 | ... 26 | public: 27 | class Iterator : public AbstractIterator { 28 | ... 29 | }; 30 | ... 31 | }; 32 | ``` 33 | 34 | ```cpp 35 | class Set { 36 | ... 37 | public: 38 | class Iterator : public AbstractIterator { 39 | ... 40 | }; 41 | ... 42 | }; 43 | ``` 44 | 45 | 46 | 47 | Now we can write functions that operate on Abstract Iterators. In this way, those functions can operate on both Sets and Lists (and any other types whose iterator inherits from Abstract Iterator). 48 | 49 | For example: 50 | 51 | ```cpp 52 | void forEach(AbsractIterator &start, AbstractIterator &end, 53 | void (*f) (int)) { 54 | while (start != end) { 55 | f (*start); 56 | ++start; 57 | } 58 | } 59 | ``` 60 | 61 | `forEach` works on List and Set. 62 | 63 | 64 | 65 | 66 | 67 | ## Observer Pattern 68 | 69 | The **Observer** design pattern is also knowns as **Dependents** or **Publish-Subscribe**. 70 | 71 | **It is intended to create a one-to-many dependency between objects such that all *observers* are notified when the state of the *subject* object being observed changes**. 72 | 73 | 74 | 75 | A publish-subscribe model. 76 | 77 | One class: **publisher/subject** - generate data/notification. 78 | 79 | One or more **subscriber/observer** - receive the data and react to it. 80 | 81 | Example: 82 | 83 | *Publisher*: spread sheet cells 84 | 85 | *Observer*: graphs - update when cells are changed; cells - formula cells that update when the cells they target are updated. 86 | 87 | The General UML: 88 | 89 | ![9-1](https://github.com/kila097/CS246_Notes/blob/main/images/9-1.jpg) 90 | 91 | 92 | 93 | The UML: 94 | the Subject typically does not need virtual methods. 95 | 96 | Subject base class "has-a" Observer base class. Object class has a virtual method: *`notify()`*, a concrete observer class will override that method. 97 | 98 | A concrete subject will have one or more function to allow it to get the stage. A concrete observer have a concrete subject. 99 | 100 | ![9-1](https://github.com/kila097/CS246_Notes/blob/main/images/9-1.png) 101 | 102 | The abstract Subject contains code common to all subjects. The abstract Observer contains the interface common to all observers. 103 | 104 | Sequence of methods calls: 105 | 106 | 1. Concrete Subject state is updated 107 | 2. Concrete Subject calls`Subject::notifyObservers()`,, which calls `notify` on all (concrete) observers currently attached. 108 | 3. Each (concrete) observer calls `getState()` (or whatever method that gets the information they need) on their subject(s), and react accordingly. 109 | 110 | 111 | 112 | Notes: 113 | 114 | - *Subject* and *Observer* classes are abstract base classes 115 | - *Subject* contains the code common to all subclasses that they inherit. There is no reason to make the `attach` and `detach` methods `virtual`, since the code will be the same for all subclasses. **There is no need to duplicate the information in the concrete subclasses** 116 | - `Subject::notifyObservers` is usually declared protected, since it should only be called by the concrete subject objects. In certain cases, it may be made public so that an external source can trigger the notifications. 117 | - The `Subject` has an aggregate relationship to the `Observer` class. Therefore, it is not responsible for destroying them when its destructor is run. 118 | - Concrete observers may dynamically attach or detach themselves from a concrete subject during the program run-time 119 | - Every time the concrete subject changes state, all of the concrete observers are notified by calling `Subject::notifyObservers`, which calls their virtual `notify` method. (`Observer::notify()` is *pure virtual*) 120 | - The subject and observer classes are **loosely coupled** since they interact and only need to know that the classes follow the specified interface i.e. thet don't need to know details of the concrete classes other than what is specified by the subject and observer public methods. New observer types only need to inherit from the Observer class. Same for new subject types. 121 | 122 | 123 | 124 | Example: Horse races, subject publishes the winner, observers are individual bettors. They declare victory when their horse wins. 125 | 126 | ```cpp 127 | // subject.h 128 | 129 | #ifndef _SUBJECT_H_ 130 | #define _SUBJECT_H_ 131 | #include 132 | #inlcude "observer.h" 133 | 134 | class Subject { 135 | std::vector observers; // pointer of observers 136 | public: 137 | Subject(); 138 | void attach( Observer *o ); 139 | void detach( Observer *o ); 140 | void notifyObservers(); 141 | virtual ~Subject() = 0; 142 | }; 143 | 144 | #endif 145 | ``` 146 | 147 | ```cpp 148 | // subject.cc 149 | 150 | #include "subject.h" 151 | 152 | Subject::Subject() {} 153 | Subject::~Subject() {} 154 | 155 | void Subject::attach( Observer *o ) { 156 | observers.emplace_back(o); 157 | } 158 | 159 | void Subject::detach( Observer *o ) { 160 | for (auto it = observers.begin(); it != observers.end(); ++it) { 161 | if (*it == o) { 162 | observers.erase(it); 163 | break; 164 | } 165 | } 166 | } 167 | 168 | void Subject::notifyObservers() { 169 | for (auto ob : observers) ob->notify(); 170 | } 171 | ``` 172 | 173 | 174 | 175 | ```cpp 176 | // observer.h 177 | 178 | #ifndef _OBSERVER_H_ 179 | #define _OBSERVER_H_ 180 | 181 | class Observer { 182 | public: 183 | virtual void notify() = 0; 184 | virtual ~Observer(); 185 | }; 186 | 187 | #endif 188 | ``` 189 | 190 | ```cpp 191 | // Observer.cc 192 | 193 | #include "observer.h" 194 | 195 | Observer::~Observer() {} 196 | ``` 197 | 198 | 199 | 200 | The bettor class: concrete observer. 201 | 202 | ```cpp 203 | // bettor.h 204 | 205 | #ifndef _BETTOR_H_ 206 | #define _BETTOR_H_ 207 | #include "observer.h" 208 | #include "horserace.h" 209 | 210 | class Bettor : public Observer { 211 | HorseRase *subject; 212 | const std::string name; 213 | const std::string myHorse; 214 | 215 | public: 216 | Bettor(HorseRase *hr, std::string name, std::string horse); 217 | void notify() override; 218 | ~Bettor(); 219 | }; 220 | 221 | #endif 222 | ``` 223 | 224 | ```cpp 225 | // bettor.cc 226 | 227 | #include 228 | #include "bettor.h" 229 | 230 | Bettor::Bettor(HorseRace *hr, std::string name, std::string horse) 231 | : subject{hr}, name{name}, myHorse{horse} 232 | { 233 | subject->attach( this ); 234 | } 235 | 236 | Bettor::~Bettr() { 237 | subject->detach( this ); // Important to detach! 238 | } 239 | 240 | void Bettor::notify() { 241 | std::cout << name 242 | << (subject->getState() == myHorse ? "wins! Off to collect." : "loses.") 243 | << std::endl; 244 | } 245 | ``` 246 | 247 | 248 | 249 | The horserace class: concrete subject 250 | 251 | ```cpp 252 | // horserace.h 253 | 254 | #ifndef _HORSERACE_H_ 255 | #define _HORSERACE_H_ 256 | #include 257 | #inlcude 258 | #include "subject.h" 259 | 260 | class HorseRace : public Subject { 261 | std::fstream in; 262 | std::string lastWinner; 263 | 264 | public: 265 | HorseRace(std::string source); 266 | ~HorseRace(); 267 | 268 | bool runRace(); // Returns true if a race was successfuly run. 269 | 270 | std::string getState(); 271 | }; 272 | 273 | #endif 274 | ``` 275 | 276 | ```cpp 277 | // horserace.cc 278 | #include 279 | #include "horserace.h" 280 | 281 | HorseRace::HorseRace(std::string source) : in{source} {} 282 | 283 | HorseRace::~HorseRace() {} 284 | 285 | bool HorseRace::runRace() { 286 | bool result {in >> lastWinner}; 287 | if (result) std::cout << "Winner: " << lastWinner << std::endl; 288 | // the concrete subject can notify observers 289 | if (result) notifyObservers(); 290 | return result; 291 | } 292 | 293 | std::string HorseRace::getState() { 294 | return lastWinner; 295 | } 296 | ``` 297 | 298 | 299 | 300 | In main, we set up our horserace: 301 | 302 | ```cpp 303 | // main.cc 304 | #include 305 | #include "bettor.h" 306 | 307 | int main(int argc, char **argv) { 308 | std:;string raceData = "race.txt"; 309 | if (argc > 1) raceData = argv[1]; 310 | 311 | HorseRace hr{raceData}; 312 | 313 | Bettor Larry{&hr, "Larry", "RunsLikeACoW"}; 314 | Better Moe{&hr, "Moe", "Molasses"}; 315 | Better Curly{&hr, "Curly", "TurtlePower"}; 316 | 317 | int count = 0; 318 | Better *Shemp = nullptr; // Shemp is going to come in for some of the races, not all 319 | 320 | while (hr.runRace()) { 321 | if (count == 2) Shemp = new Bettor{&hr, "Shemp", "GreasedLightning"}; 322 | if (count == 5) delete Shemp; 323 | ++count; 324 | } 325 | if (count < 5) delete Shemp; 326 | } 327 | ``` 328 | 329 | - since "bettor.h" already includes "horserace.h", we don't need to explicitly include it, though we could 330 | - the concrete subject, hr, is initialized with the race results, i.e. the name of the winner for each race 331 | 332 | 333 | 334 | The information is **pulled** by the observer. The subject notifies the observer that the event has occurred, and it's up to the observer to retrieve the desired information from the subject. 335 | 336 | 337 | 338 | 339 | 340 | 341 | 342 | 343 | 344 | ## Decorator Pattern 345 | 346 | Suppose that we want to enhance an object, e.g. add or change behaviour at runtime. 347 | 348 | The **Decorator** design pattern in intended to let you add functionality or features to an object at *run-time* rather than to the class as a whole. These functionalities or features might also be withdrawn. We will end up having lots of little objects that look rather similar to each other, and differ only in how they're connected. 349 | 350 | For example: a windowing system (that display windows on your computer), we could start with a basic window, and then add a scrollbar, and then maybe a menu. We want to be able to choose which features to apply to which windows at runtime. 351 | 352 | Other examples: 353 | 354 | - Telephones: call waiting, caller ID, call forwarding, distinctive ringing, automatic callback, speed dialing, redial 355 | - Word processors: track changes, paragraph format, line spacing, highlighting, text font, text colour, sticky notes, spell checking 356 | - Games: character's appearance, hair, hat, glasses, beard, moustache, jewelry, piercings, tattoos, clothing 357 | 358 | 359 | 360 | 361 | 362 | The UML: 363 | Start with a base class, component, it can have one or more virtual methods. We need two derived classes, one of them is the concrete component of the basic component (override the methods in base class). Then, we have a second derived class, this is also an *abstract* class, called the **decorator**. The decorator class also need to have operations, and it has a component (aggregation). It's operation just calls component->operation(). 364 | 365 | Every decorator has a component, that component may be a concrete component or our basic concrete component, or it may be a concrete decorator. 366 | 367 | ![9-2](https://github.com/kila097/CS246_Notes/blob/main/images/9-2.png) 368 | 369 | - The decorator base class has (can owns) component (will have a pointer to a component). It can either be decorating a concrete base component, or something that has already be decorated (concreate decorator A & B). 370 | - This lets us build a linked list of decoration objects by having a pointer to a Component. Since all of the decoration classes inherit from the Component, we don't need wo know what is in the linked list once it's built since the `operation` we are invoking is `virtual` (likely pure virtual) in the Component class. 371 | - Since the ConcreteComponent inherits from Component, the linked list thus terminates with a concrete component object. 372 | - Each call to the operation in a decoration object ends up invoking the `operation` in the next component. 373 | - In this way, we can build up values or actions by *delegating* the work to the objects of which the decorated object is composed. 374 | - New functionalities are introduced by adding new subclasses, not modifying existing code, which reduces the chance of introducing errors 375 | 376 | 377 | 378 | The component class defines the interface: the operations that your objects will provide. Concrete component implements the interface, and is our base starting object. The decorator class inherits from component, AND has a component. All concrete decorators inherit from decorator (they are both decorator and component, because a decorator inherits from component). Therefore, every decorator IS-A component, and HAS (or owns) A component. 379 | 380 | e.g. A window with a scrollbar is a scrollbar object (which is a type of window) and has a pointer to the underlying plain window. The scrollbar *decorates* the plain window. A scrollbar with a menu is a menu with a pointer to the underlying scrollbar which itself has a pointer to the underlying plain window. (the order isn't necessarily important: we can have scrollbar -> menu -> plain window, or menu -> scrollbar -> plain window). 381 | 382 | Since these all inherit from a base window type, all operations that work on windows work on them. 383 | 384 | The UML of window example 385 | 386 | ![9-7](https://github.com/kila097/CS246_Notes/blob/main/images/9-7.jpg) 387 | 388 | 389 | 390 | In order to build a linked-list, all of our effects need to have a pointer to the abstract base class type since we don't know what sort of object they will actually point to at run-time. 391 | 392 | Since all of our effects need the pointer, and we don't want our concrete component class to have it, since it is the final node in the list. We would prefer to introduce an intermediate abstract base class, `Decorator`, that: 393 | 394 | - inherits from `Component` 395 | - holds the pointer, and 396 | - still has pure virtual methods for operation() 397 | 398 | The effect classes will inherit from the Decorator. 399 | 400 | 401 | 402 | Example: Pizza: customers adding ingredients to it, the cost of pizza depends upon the items ordered. 403 | 404 | 9-3 405 | 406 | Abstract base Component class 407 | 408 | ```cpp 409 | // pizza.h 410 | #ifndef _PIZZA_H_ 411 | #define _PIZZA_H_ 412 | #include 413 | 414 | // abstract component 415 | class Pizza { 416 | public: 417 | virtual float price() const = 0; 418 | virtual std::string description() const = 0; 419 | virtual ~Pizza(); // destructor always need to be virtual 420 | }; 421 | 422 | #endif 423 | 424 | // pizza.cc 425 | #include "pizza.h" 426 | 427 | Pizza::~Pizza() {} 428 | ``` 429 | 430 | Concrete Component class: 431 | 432 | ```cpp 433 | // crustandsauce.h 434 | #ifndef _CRUSTANDSAUCE_H_ 435 | #define _CRUSTANDSAUCE_H_ 436 | #include "pizza.h" 437 | 438 | // concrete component 439 | class CrustAndSause : public Pizza { 440 | // override the member functions 441 | public: 442 | float price() const override; 443 | std::string description() const override; 444 | }; 445 | 446 | #endif 447 | 448 | // crustandsauce.cc 449 | #include "curstandsauce.h" 450 | 451 | float CrustAndSauce::price() const { return 5.99; } 452 | 453 | std::string CrustAndSauce::description() const { return "Pizza"; } 454 | ``` 455 | 456 | 9-4 457 | 458 | ![9-4](https://github.com/kila097/CS246_Notes/blob/main/images/9-4.jpg) 459 | 460 | 461 | The Decorator inherits from abstract component: 462 | ```cpp 463 | // decorator.h 464 | #ifndef _DECORATOR_H_ 465 | #define _DECORATOR_H_ 466 | #include "pizza.h" 467 | 468 | class Decorator : public Pizza { 469 | protected: 470 | Pizza *component; 471 | public: 472 | Decorator(Pizza *component); // need a decorator constructor 473 | virtual ~Decorator(); // means we are taking ownship of the component, we need to change this to a own-a relationship in UML 474 | }; 475 | 476 | 477 | // decorator.cc 478 | #include "pizza.h" 479 | #include "decorator.h" 480 | 481 | Decorator::Decorator(Pizza *component) : component{component} {} 482 | 483 | Decorator::~Decorator() { delete component; } // need to delete component because it is a pointer to a node 484 | ``` 485 | 486 | The abstract decorator doesn't actually override the price or description, it is the concrete decorators that overrides it. The decorator is still abstract. 487 | 488 | 489 | 490 | Let's write the concrete decorator classes: 491 | 492 | ```cpp 493 | // topping.h 494 | #ifndef _TOPPING_H_ 495 | #define _TOPPING_H_ 496 | #inlcude "pizza.h" 497 | #include "decorator.h" 498 | 499 | class Pizza; // forward declaration here 500 | 501 | class Topping : public Decorator { 502 | std::string theTopping; // we know what our topping is 503 | const float thePrice; 504 | public: 505 | Topping(std::string topping, Pizza *component); 506 | float price() const override; 507 | std::string description() const override; 508 | }; 509 | 510 | #endif 511 | 512 | 513 | // topping.cc 514 | #include "topping.h" 515 | #include "pizza.h" 516 | 517 | Topping::Topping(std::string topping, Pizza *component) 518 | : Decorator{component}, theTopping{topping}, thePrice{0.75} {} 519 | 520 | float Topping::price() const { return component->price() + thePrice; } 521 | // we might be decorating something that has already been decorated 522 | std::string Topping::description() const { 523 | return component->description() + " with " + theTopping; 524 | } 525 | ``` 526 | 527 | Other concrete decorator classes are similar: 528 | 529 | ```cpp 530 | // stuffedcrust.h 531 | #ifndef _STUFFEDCRUST_H_ 532 | #define _STUFFEDCRUST_H_ 533 | #include "pizza.h" 534 | #include "decorator.h" 535 | #include 536 | 537 | 538 | class StuffedCrust : public Decorator { 539 | // a StuffedCrush is also a Pizza because a Decorator is a Pizza 540 | public: 541 | StuffedCrust(Pizza *component); 542 | float price() const override; 543 | std::string description() const override; 544 | }; 545 | 546 | #endif 547 | 548 | 549 | // stuffedcrust.cc 550 | #include "stuffedcrust.h" 551 | 552 | StuffedCrust::StuffedCrust(Pizza *component) : Decorator{component} {} 553 | 554 | // let's see the way we "decorate" the price and description 555 | 556 | float StuffedCrust::price() const { 557 | return component->price() + 2.69; // 2.69 plus the price of whatever the thing we are decorating 558 | } 559 | 560 | std::string StuffedCrust::description() const { 561 | return compoent->description() + " with stuffed crust"; // concatenate the decorated description 562 | }; 563 | ``` 564 | 565 | ```cpp 566 | // dippingsauce.h 567 | #ifndef _DIPPINGSAUCE_H_ 568 | #define _DIPPINGSAUCE_H_ 569 | #include "pizza.h" 570 | #include "decorator.h" 571 | #include 572 | 573 | class DippingSauce : public Decorator { 574 | std::string flavour; 575 | public: 576 | DippingSauce(std::string flavour, Pizza *component); 577 | float price() const override; 578 | std::string description() const override; 579 | }; 580 | 581 | // dippingsauce.cc 582 | #include "dippingsauce.h" 583 | 584 | DippingSauce::DippingSauce(std::string flavour, Pizza *p) 585 | : Decorator{p}, flavour{flavour} {} 586 | 587 | float DippingSauce::price() { return component->price() + 0.30; } 588 | 589 | std::string DippingSauce::description() { 590 | return component->description() + " with " + flavour + " dipping sauce"; 591 | } 592 | ``` 593 | 594 | 595 | 596 | How do we use them? 597 | 598 | ```cpp 599 | // in main.cc 600 | #include 601 | #include 602 | #include 603 | #include "pizza.h" 604 | #include "topping.h" 605 | #include "stuffedcrust.h" 606 | #include "dippingsauce.h" 607 | #include "crustandsauce.h" 608 | 609 | int main() { 610 | Pizza *myPizzaOrder[3]; 611 | 612 | myPizzaOrder[0] = new Topping{"pepperoni", 613 | new Topping {"cheese", new CrustAndSauce}}; 614 | myPizzaOrder[1] = new StuffedCrust{ 615 | new Topping{"cheese", new Topping{"mushrooms", new CrustAndSauce}}}; 616 | myPizzaOrder[2] = new DippingSauce{"garlic", 617 | new Topping{"cheese", 618 | new Topping {"cheese", 619 | new Topping {"cheese", 620 | new Topping {"cheese", 621 | new CrustAndSauce}}}}}; 622 | float total = 0.0; 623 | for (int i = 0; i < 3; ++i) { 624 | std::cout << myPizzaOrder[i]->description() 625 | << ": $" << std::fixed << std::showpoint << std::setprecision(2) 626 | << myPizzaOrder[i]->price() << std::endl; 627 | total += myPizzaOrder[i]->price(); 628 | } 629 | std::cout << std::endl << "Total cost: $" << std::fixed << std::showpoint 630 | << std::setprecision(2) << total << std::endl; 631 | 632 | for (int i = 0; i < 3; ++i) delete myPizzaOrder[i]; 633 | 634 | // or we could do: 635 | Pizza p1 = new CrustAndSauce; 636 | p1 = new Topping{"cheese", p1}; // storing p1 ptr in this new Topping, decorate it with the new topping 637 | p1 = new Topping{"pineapple", p1}; 638 | p1 = new StuffedCrust{p1}; 639 | 640 | cout << p1->description() << " costs " << p1->price() << endl; 641 | delete p1; 642 | 643 | } 644 | 645 | ``` 646 | 647 | - note that we first create the object CrustAndSauce, which is a plain pizza. The creation of a decorated pizza is the creation of a singly linked list. 648 | 649 | 650 | 651 | 652 | 653 | 654 | 655 | ## Factory Method Pattern 656 | 657 | **Factory Method** design pattern provides an interface for object creation, but lets the subclasses decide which object to create. It is also known as the **Virtual Constructor**. 658 | 659 | Abstract a way the policy of object creations. We want different creation for different scenarios. 660 | 661 | The general UML: 662 | 663 | ![9-12](https://github.com/kila097/CS246_Notes/blob/main/images/9-12.jpg) 664 | 665 | 666 | 667 | Notes: 668 | 669 | - Encapsulates the object creation as the part of the design that changes, which is why it's a virtual method in the `Creator` super class. It may not necessarily be a pure virtual method if it chooses to provide a default implementation to use. 670 | - `Creator` superclass encapsulates the operations other than creation that can be applied to a product i.e.`anOperation()` 671 | - A valid variation is to add a parameter to the `newProduct()` method. 672 | 673 | 674 | 675 | Problem: write a video game with 2 kinds of enemies: turtles and bullets. The system randomly sends turtles and bullets, but bullets come more frequently in castle levels. 676 | 677 | At an easy/normal level, you want the random creation of enemies to be 70% bullets and 30% turtles. 678 | 679 | ![9-6](https://github.com/kila097/CS246_Notes/blob/main/images/9-6.png) 680 | 681 | Since we never know exactly which enemy comes next, we can't call their constructors directly. Moreover, we want customizable behaviour for the policy of objects (enemy) creation. 682 | 683 | We create a virtual method (`createEnemy`) to do the thing we want to do differently for each type. 684 | 685 | 686 | 687 | So instead, we put a **factory method** in level that creates enemies. 688 | 689 | ```cpp 690 | // level.h 691 | 692 | class Enemy; // forward declaration of Enemy here 693 | 694 | class Level { 695 | public: 696 | // factory method 697 | virtual Enemy *createEnemy() = 0; // pure virtual factory method 698 | ... 699 | }; 700 | 701 | 702 | // level.cc 703 | #include "level.h" 704 | // implements whatever isn't pure virtual 705 | ``` 706 | 707 | 708 | 709 | ```cpp 710 | class NormalLevel : public Level { 711 | public: 712 | Enemy *createEnemy() override { 713 | double x = rand(); // generate a random # between 1.0 and 0.0 714 | if (x <= 0.7) { 715 | return new Turtle{...}; 716 | } else { 717 | return new Bullet{...}; 718 | } 719 | }; // 70% of enemies on normal levels are turtles, 30% of them of bullets 720 | ``` 721 | 722 | 723 | 724 | ```cpp 725 | class Castle : public Level { 726 | public: 727 | Enemy *createEnemy() override { 728 | // code for 50% turtles, 729 | // and 50% bullets 730 | } 731 | }; 732 | ``` 733 | 734 | 735 | 736 | ```cpp 737 | // the use of this 738 | 739 | Level *curLevel = new NormalLevel; 740 | Enemy *e = curLevel->createEnemy(); 741 | ``` 742 | 743 | We can create the enemies without needing to know anything about how the choice of which enemy to create is made. 744 | 745 | The pattern is also known as the **virtual constructor pattern**. 746 | 747 | 748 | 749 | 750 | 751 | ## Template Method Pattern 752 | 753 | The **Template Method** design pattern defines the steps of an algorithm in an operation, but lets the subclasses redefine certain steps though not the overall algorithm's structure. 754 | 755 | Used when we want a subclass to override *some* aspects of method behaviour, but keep other aspects the same. 756 | 757 | The general UML: 758 | 759 | ![9-13](https://github.com/kila097/CS246_Notes/blob/main/images/9-13.jpg) 760 | 761 | 762 | 763 | Notes: 764 | 765 | - The abstract base class contains the refactored common behaviour for all subclasses to remove duplicate code 766 | - Subclasses can only override the provided *hooks*, the virtual methods 767 | - The virtual methods may provide a default implementation, or be pure virtual 768 | - The method that contains the algorithm is not `virtual`, the steps and their order may not be changed! 769 | 770 | 771 | 772 | Example: there are red turtles and there are green turtles. All turtles have a head, a shell, and feet. 773 | 774 | ```cpp 775 | class Turtle { 776 | public: 777 | void draw() { 778 | drawHead(); 779 | drawShell(); // must be specicalized: red or green 780 | drawFeet(); 781 | } 782 | private: 783 | void drawHead() {...} // can be implemented in this base class, 784 | void drawFeet() {...} // because we want them to stay the same 785 | virtual void drawShell() = 0; // yes, virtual methods can be private 786 | }; 787 | ``` 788 | 789 | Note: it is perfectly legal to have a `private` virtual method. 790 | 791 | Our concrete red and green Turtles: 792 | 793 | ```cpp 794 | class RedTurtle : public Turtle { 795 | void drawShell() override { 796 | // draw a red shell 797 | } 798 | }; 799 | 800 | class GreenTurtle : public Turtle { 801 | void drawShell() override { 802 | // draw a green shell 803 | } 804 | } 805 | ``` 806 | 807 | Subclasses can't change the way a turtle is drawn, that is, head then shell then feet, but can change the way in which the shell is drawn. 808 | 809 | 810 | 811 | 812 | 813 | ## Non-virtual Interface Idiom 814 | 815 | Extension: **Non-virtual interface (NVI) idiom** 816 | 817 | A public virtual method is really two things at once. 818 | 819 | - an interface to the client 820 | - indicates provided behaviour and pre/post conditions; promises class invariants (i.e. what properties must hold or must be true both before and after the method executes for all instances of the class) 821 | - an interface to the subclasses 822 | - a place for the subclasses (virtual) to add their own "**hook**" to inject specialized behaviour 823 | 824 | Problem is that it is hard to separate these two ideas if they are wrapped up in the same function declaration. 825 | 826 | - What if you later want to separate the specialized behaviour into two methods? Maybe with some non-customizable steps in between? But also don't want to change the public interface. 827 | - How can we make sure that all overriding methods conform to our pre and post conditions and class invariants? 828 | 829 | 830 | 831 | The **NVI idiom** says: 832 | 833 | - all public methods should be **non-virtual** 834 | - all **virtual methods should be private** (or at least protected) 835 | - Except for one method, obviously, the **destructor**. (if the dtor is private, then objects of this type can't actually be destroyed). It needs to be virtual so we can always call the right destructor (in base class or in subclasses). So the destructor should be both *public* and *virtual*. 836 | 837 | 838 | 839 | Example: Digital media 840 | 841 | ```cpp 842 | // not NVI 843 | class DigitalMedia { 844 | public: 845 | virtual void play() = 0; 846 | virtual ~DigitalMedia(); 847 | }; 848 | 849 | // Translate to NVI 850 | class DigitalMedia { 851 | public: 852 | // no longer have a virtual method, all public methods non virtual 853 | void play() { doPlay(); } 854 | virtual ~DigitalMedia(); // dtor need to be public and virtual 855 | private: 856 | virtual void doPlay() = 0; 857 | }; 858 | ``` 859 | 860 | 861 | 862 | Now, if we need to exert extra control over the `play()` method, we can do it, **without changing the interface**. 863 | 864 | - we're getting sued for not checking for DRM(media licensing), so we need all of our derived classes to do so. 865 | 866 | We can just rewrite `play()` (`doPlay()` cannot be changed) 867 | 868 | ```cpp 869 | void play { 870 | checkLicense(); 871 | doPlay(); 872 | } 873 | ``` 874 | 875 | - We could add more virtual hooks if we want, for example: `showCoverArt` (album cover for music, DVD cover, etc.) 876 | 877 | ```cpp 878 | public: 879 | void play() { 880 | checkLicense(); 881 | showCoverArt(); 882 | } 883 | private: 884 | virtual void showCoverArt() = 0; 885 | void checkLicense() { // this is non-virtual 886 | ... 887 | throw BadDRM(); 888 | } 889 | ``` 890 | 891 | - All of this without changing the public interface 892 | 893 | 894 | 895 | It's much easier to take this kind of control over our virtual methods from the beginning by starting with the NVI idiom, rather than trying to take control back later (because we have to change all the derived classes). 896 | 897 | The NVI idiom extends the Template Method Pattern buy putting EVERY virtual method inside a non-virtual wrapper. There is essentially no disadvantages for following the NVI. As a good compiler will optimize out the extra function call. (and there is a huge upside) 898 | 899 | 900 | 901 | 902 | 903 | 904 | 905 | ## Visitor Pattern (March 22) 906 | 907 | The **Visitor** design pattern allows the programmer to apply an operation to be performed upon the elements contained in an object structure. New 908 | 909 | For implementing double dispatch, virtual methods are chosen based on the actual run-time type of the object they are called on. What if you want to choose a method based on two objects? 910 | 911 | 912 | 913 | The General UML Model: 914 | 915 | ![9-9](https://github.com/kila097/CS246_Notes/blob/main/images/9-9.jpg) 916 | 917 | 918 | 919 | 920 | 921 | Motivating Example: Striking enemies with weapon: 922 | 923 | What weapon we have available will depend upon what has happened so far in the game, and the various types of weapons available will have varying effects on the enemies. i.e. some weapons may be more effective acting upon certain enemies than others. 924 | 925 | ![9-8](https://github.com/kila097/CS246_Notes/blob/main/images/9-8.png) 926 | 927 | We want something like: 928 | 929 | ```cpp 930 | virtual void (Enemy, Weapon)::strike(); 931 | // or 932 | virtual void strike(Enemy &e, Weapon &w); 933 | ``` 934 | 935 | If we put the method in Enemy: `virtual void Enemy::string(Weapon &w);`, we choose the method based on Enemy, but not weapon. 936 | 937 | If we put the method in Weapon, vice versa. This is called **dynamic dispatch**, and is implemented in C++ as **single dispatch**. i.e., can be only performed upon one object by looking up its actual type at run-time. 938 | 939 | 940 | 941 | We need a technique called **double dispatch**, which requires us to *combine* both **overloading** and **overriding** (single dispatch). 942 | 943 | We pick Enemy class to apply **overriding**, we then apply **overloading** to the Weapon class. 944 | 945 | ```cpp 946 | // enemy.h 947 | class Enemy { 948 | public: 949 | // override i.e. dynamic dispatch 950 | virtual void beStruckBy(Weapon &w) = 0; 951 | }; 952 | ``` 953 | 954 | The revised class model is: 955 | 956 | 957 | 958 | ![9-10](https://github.com/kila097/CS246_Notes/blob/main/images/9-10.jpg) 959 | 960 | 961 | 962 | ```cpp 963 | // turtle.h 964 | class Turtle : public Enemy { 965 | public: 966 | void beStruckBy(Weapon &w) override { 967 | w.strike(*this); 968 | // *this is a Turtle 969 | } 970 | }; 971 | 972 | 973 | // bullet.h 974 | class Bullet : public Enemy { 975 | public: 976 | void beStruckBy(Weapon &w) { 977 | w.strike(*this); 978 | // *this is a Bullet 979 | } 980 | } 981 | ``` 982 | 983 | 984 | 985 | Now we use *overloading* to get the behaviour we want: 986 | 987 | ```cpp 988 | // weapon.h 989 | class Weapon { 990 | public: 991 | // overload for other class hierarchy 992 | virtual void strike(Turtle &t) = 0; 993 | virtual void strike(Bullet &b) = 0; 994 | }; 995 | ``` 996 | 997 | 998 | 999 | ```cpp 1000 | // stick.h 1001 | class Stick : public Weapon { 1002 | public: 1003 | void strike(Turtle &t) { 1004 | // code to hit a turtle with a stick 1005 | } 1006 | void strike(Bullet &b) { 1007 | // code to hit a bullet with a stick 1008 | } 1009 | 1010 | }; 1011 | 1012 | // rock.h 1013 | class Rock : public Weapon { 1014 | public: 1015 | void strike(Turtle &t) { 1016 | // code to hit a turtle with a rock 1017 | } 1018 | void strike(Bullet &b) { 1019 | // code to hit a bullet with a rock 1020 | } 1021 | 1022 | }; 1023 | ``` 1024 | 1025 | 1026 | 1027 | The client code: 1028 | 1029 | ```cpp 1030 | Enemy *e = new Bullet{...}; // create a bullet 1031 | Weapon *w = new Rock{...}; // create a rock 1032 | 1033 | e->beStruckBy( *w ); 1034 | ``` 1035 | 1036 | What happens (if we call `e->beStruckBy( *w )`) ? 1037 | 1038 | - first, the program has to determine the actual object type for e 1039 | 1040 | - since e is pointing to a Bullet object, it ends up invoking `Bullet::beStruckBy()` (virtual dispatch), passing in *w as the parameter called w 1041 | - Once again, we have to determine what the actual object type that w is bound to, which is a rock. (Rock has overloaded the strike method), it c alls `Weapon::strike()` on *this, since it is in the bullet method, `*this` is a `Bullet` 1042 | - therefore the Bullet version run at compile time 1043 | - the virtual method strike(Bullet &b) resolves to `Rock::strike(Bullet &b)` 1044 | 1045 | **This ability to perform double dispatch is at the core of the Visitor design pattern**. 1046 | 1047 | 1048 | 1049 | Code example: 1050 | 1051 | Visitor can be used to add functionality to existing classes in a hierarchy without changing or recompiling those classes themselves - a class can support "Houns" for visitors. e.g. Books: 1052 | 1053 | Add a `BookVisitor` class and a concrete `Catalogue` class to the hierarchy that can be used to traverse the collection and perform the following actions: 1054 | 1055 | - count the number of distinct book authors 1056 | - count the number of distinct text topics, and 1057 | - count the number of distinct comic heroes 1058 | 1059 | ![9-11](https://github.com/kila097/CS246_Notes/blob/main/images/9-11.jpg) 1060 | 1061 | 1062 | 1063 | ```cpp 1064 | // BookVisitor.h 1065 | 1066 | #ifndef _BOOKVISITOR_H_ 1067 | #define _BOOKVISITOR_H_ 1068 | 1069 | class Book; // forward declarations 1070 | class Text; 1071 | class Comic; 1072 | 1073 | 1074 | class BookVisitor { // weapon 1075 | public: 1076 | virtual void visit(Book &b) = 0; // strike 1077 | virtual void visit(Text &t) = 0; 1078 | virtual void visit(Comic &c) = 0; 1079 | virtual ~BookVisitor(); 1080 | }; 1081 | 1082 | #endif 1083 | ``` 1084 | 1085 | 1086 | 1087 | ```cpp 1088 | // BookVisitor.cc 1089 | 1090 | #include "BookVisitor.h" 1091 | #include "book.h" 1092 | #inlcude "text.h" 1093 | #include "comic.h" 1094 | 1095 | BookVisitor::~BookVisitor() {} 1096 | ``` 1097 | 1098 | 1099 | 1100 | ```cpp 1101 | // catalogue.h 1102 | 1103 | #ifndef _CATALOGUE_H_ 1104 | #define _CATALOGUE_H_ 1105 | #include 1106 | #include 1107 | 1108 | #include "BookVisitor.h" 1109 | class CatalogueVisitor : public BookVisitor { 1110 | std::map theCatalogue; 1111 | 1112 | public: 1113 | std::map getResult() const; 1114 | virtual void visit(Book &b) override; 1115 | virtual void visit(Text &t) override; 1116 | virtual void visit(Comic &c) override; 1117 | }; 1118 | 1119 | #endif 1120 | ``` 1121 | 1122 | 1123 | 1124 | ```cpp 1125 | // catalogue.h 1126 | 1127 | #include "catalogue.h" 1128 | #include "book.h" 1129 | #include "text.h" 1130 | #include "comic.h" 1131 | using namespace std; 1132 | 1133 | map CatalogueVisitor::getResult() const { return theCatalogue; } 1134 | void CatalogueVisitor::visit(Book &b) { ++theCatalogue[b.getAuthor()]; } 1135 | void CatalogueVisitor::visit(Text &t) { ++theCatalogue[t.getTopic()]; } 1136 | void CatalogueVisitor::visit(Comic &c) { ++theCatalogue[c.getHero()]; } 1137 | 1138 | ``` 1139 | 1140 | 1141 | 1142 | 1143 | ```cpp 1144 | class Book { // Enemy 1145 | public: 1146 | ... 1147 | virtual void accept(BookVisitor &v) { v.visit(*this); } 1148 | }; 1149 | ``` 1150 | 1151 | 1152 | 1153 | 1154 | ```cpp 1155 | class Text : public Book { 1156 | public: 1157 | ... 1158 | void accept(BookVisitor &v) { v.visit(*this); } 1159 | }; 1160 | // comic is the same 1161 | class Comic : public Book { 1162 | public: 1163 | ... 1164 | void accept(BookVisitor &v) { v.visit(*this); } 1165 | } 1166 | ``` 1167 | 1168 | 1169 | 1170 | Application: Track how many of each book type we have. Group Books by Author, Texts by topic, Comics by hero. 1171 | 1172 | We use a `map`, we could add to the Book hierarchy a `virtual void updateMap(map &m);` : 1173 | 1174 | ```cpp 1175 | for (Book &b : myBooks) { b.updateMap(myMap); } 1176 | ``` 1177 | 1178 | 1179 | 1180 | Our main program consists of: 1181 | 1182 | ```cpp 1183 | // main.cc 1184 | 1185 | #include 1186 | #include 1187 | #include "book.h" 1188 | #include "text.h" 1189 | #include "comic.h" 1190 | #include "catalogue.h" 1191 | 1192 | using namespace std; 1193 | 1194 | int main() { 1195 | std::vector collection { 1196 | new Book{ "War and Peace", "Tolstoy", 5000 }, 1197 | new Book{ "Peter Rabbit", "Potter", 50 }, 1198 | new Text{ "Programming for Beginners", "??", 200, "BASIC" }, 1199 | new Text{ "Programming for Big Kids", "??", 200, "C++" }, 1200 | new Text{ "Annotated Reference Manual", "??", 200, "C++" }, 1201 | new Comic{ "Aquaman Swims Again", "??", 20, "Aquaman" }, 1202 | new Comic{ "Clark Kent Loses His Glasses", "??", 1203 | 20, "Superman" }, 1204 | new Comic{ "Superman Saves the Day", "??", 20, "Superman" } 1205 | }; 1206 | 1207 | CatalogueVisitor v; 1208 | 1209 | for (auto &b : collection) b->accept(v); 1210 | 1211 | for (auto &i : v.theCatalogue) 1212 | cout << i.first << " " << i.second << endl; 1213 | 1214 | for (auto &b : collection) delete b; 1215 | } 1216 | ``` 1217 | 1218 | - The program creates a vector of Book pointers that is initialized with a set of Book, Text, and Comic objects allocated on the heap 1219 | - A CatalogueVisitor object, v, is created 1220 | - Iterate over the book collection, asking each element to accept the concrete visitor object v, Note that we are using Iterator to iterate over the STL vector and map containers 1221 | - v adds 1 to the author/hero/topic count if it is being accepted by a book/comic/text object 1222 | - If the author/hero/topic didn't previously exist, the map element is created and the count is set to 1 1223 | - We then iterate through the map in v, printing out the information it is indexed by (author/hero/topic) and the associated count 1224 | - We then free the heap allocated memory 1225 | 1226 | 1227 | 1228 | Book.h include BookVisitor.h, which includes Text.h, which includes Book.h - a *circular include dependency*. 1229 | 1230 | ```cpp 1231 | // book.h 1232 | #include "BookVisitor.h" 1233 | 1234 | 1235 | 1236 | // BookVisitor.h 1237 | #include "book.h" 1238 | #include "text.h" 1239 | #include "comic.h" 1240 | 1241 | 1242 | // text.h 1243 | #include "book.h" 1244 | #include "BookVisitor.h" 1245 | ``` 1246 | 1247 | 1248 | 1249 | Because of the include guard, text.h doesn't actually get a copy of book.h included, so the compiler doesn't know what book is when we try to inherit from it in text. 1250 | 1251 | But are all of these includes really necessary? 1252 | 1253 | 1254 | 1255 | ## Compilation dependencies 1256 | 1257 | When does a compilation dependency exist, i.e. when does one file *really* need to include another one? 1258 | 1259 | 1260 | 1261 | Consider: 1262 | 1263 | ```cpp 1264 | class A {...}; // a.h 1265 | 1266 | class B : public A { 1267 | ... 1268 | }; // b.h 1269 | 1270 | class C : { 1271 | A myA; 1272 | ... 1273 | }; // c.h 1274 | 1275 | class D { 1276 | A * myApyr; 1277 | ... 1278 | }; // d.h 1279 | 1280 | class E { 1281 | A f(A x); 1282 | ... 1283 | }; // e.h 1284 | 1285 | class F { 1286 | A f(A x) {... x.someMethod() ...} 1287 | ... 1288 | }; // f.h 1289 | ``` 1290 | 1291 | 1292 | 1293 | Which of these require an `#include "a.h"` and which only need a forward declaration `class A;` 1294 | 1295 | B: need to include a.h 1296 | 1297 | - is a subclass inherit from A 1298 | - B has a bigger size than A, the size of B object is dependent on A object 1299 | 1300 | 1301 | 1302 | C : need to include a.h 1303 | 1304 | - size of C object depend on A object 1305 | 1306 | 1307 | 1308 | D : forward declaration 1309 | 1310 | - a pointer, so we don't need to know about the size 1311 | 1312 | 1313 | 1314 | E : forward declaration 1315 | 1316 | - a function that takes an A and returns an A 1317 | 1318 | - we don't need to know the size of A, 1319 | - we don't need to know any details of A 1320 | 1321 | 1322 | 1323 | F : need to include a.h 1324 | 1325 | - we need to know x.someFunction, and we can only know this by knowing the function of A 1326 | - we need to know the implementation of A, so we need to include a.h 1327 | 1328 | 1329 | 1330 | 1331 | 1332 | If we need to know the *details of a class (size, functions)*, we need to include. 1333 | 1334 | If we only need to know the things, so we can talk about it, then we only need a forward declaration 1335 | 1336 | 1337 | 1338 | Classes B and C have **true compilation dependencies**, the compiler must know the size. It need to know how big a A is in order to know how big B or C are. Also, B needs to know about the methods (interface). 1339 | 1340 | Classes D and E do not have a true compilation dependency. A forward declaration will suffice. All pointers are the same size, and function prototypes (declaration of a function) are only used for type-checking purposes (since they are not implemented yet). However, we can get ourselves into linking problems for class D. 1341 | 1342 | Class F need to know information about A because it is actually implementing a function. Namely, it needs to at least know that `A::someMethod()` exists. 1343 | 1344 | 1345 | 1346 | **If there is no true compilation dependencies necessitated by the code, don't introduce one by having unnecessary #includes.** 1347 | 1348 | Additionally, if class A changes, only classes A, B, C, and F need recompilation. 1349 | 1350 | In the implementations files (.cc) of class D and E, the dependency will be stronger. 1351 | 1352 | ```cpp 1353 | // d.cc 1354 | #include "a.h" 1355 | 1356 | void D::f() { 1357 | MyAptr->someMethod(); // need to know about class A here, so a true dependency exists. 1358 | } 1359 | ``` 1360 | 1361 | When possible, forward declare in the header, and include in the implementation as necessary. 1362 | 1363 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # CS246_Notes 2 | CS246 course notes from the University of Waterloo, taken at Winter 2022, taught by Rob Hackman. 3 | 4 | ## Topics 5 | 6 | [1. Linux](https://github.com/kila097/CS246_Notes/blob/main/1.%20Linux.md) 7 | 8 | 9 | [2. Software Testing](https://github.com/kila097/CS246_Notes/blob/main/2.%20Software%20Testing.md) 10 | 11 | 12 | [3. Basic C++](https://github.com/kila097/CS246_Notes/blob/main/3.%20Basic%20C%2B%2B.md) 13 | 14 | 15 | [4. Object Oriented Programming](https://github.com/kila097/CS246_Notes/blob/main/4.%20Object%20Oriented%20Programing.md) 16 | 17 | 18 | [5. Encapsulation and Iterator Pattern](https://github.com/kila097/CS246_Notes/blob/main/5.%20Encapsulation%20and%20Introduction%20to%20Design%20Patterns.md) 19 | 20 | 21 | [6. Relationship and Inheritance](https://github.com/kila097/CS246_Notes/blob/main/6.%20Relationships%20and%20Inheritance.md) 22 | 23 | 24 | [7. Standard Template Library](https://github.com/kila097/CS246_Notes/blob/main/7.%20The%20Standard%20Template%20Library.md) 25 | 26 | 27 | [8. Handling with Exceptions](https://github.com/kila097/CS246_Notes/blob/main/8.%20Error%20Handling%20with%20Exceptions.md) 28 | 29 | 30 | [9. Advanced Design Patterns](https://github.com/kila097/CS246_Notes/blob/main/9.%20Design%20Patterns.md) 31 | 32 | 33 | [10. Advanced C++](https://github.com/kila097/CS246_Notes/blob/main/10.%20Advanced%20C%2B%2B.md) 34 | 35 | 36 | [11. VTable and Multiple Inheritance](https://github.com/kila097/CS246_Notes/blob/main/11.%20Vtables%20and%20Multiple%20Inheritance.md) 37 | 38 | 39 | TBC, 40 | -------------------------------------------------------------------------------- /images/1-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kila097/CS246_Notes/b34c1597a5e5958b749e7855255c860247f98da6/images/1-1.png -------------------------------------------------------------------------------- /images/1-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kila097/CS246_Notes/b34c1597a5e5958b749e7855255c860247f98da6/images/1-2.png -------------------------------------------------------------------------------- /images/10-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kila097/CS246_Notes/b34c1597a5e5958b749e7855255c860247f98da6/images/10-1.png -------------------------------------------------------------------------------- /images/11-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kila097/CS246_Notes/b34c1597a5e5958b749e7855255c860247f98da6/images/11-1.png -------------------------------------------------------------------------------- /images/11-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kila097/CS246_Notes/b34c1597a5e5958b749e7855255c860247f98da6/images/11-2.png -------------------------------------------------------------------------------- /images/11-3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kila097/CS246_Notes/b34c1597a5e5958b749e7855255c860247f98da6/images/11-3.png -------------------------------------------------------------------------------- /images/11-4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kila097/CS246_Notes/b34c1597a5e5958b749e7855255c860247f98da6/images/11-4.png -------------------------------------------------------------------------------- /images/11-5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kila097/CS246_Notes/b34c1597a5e5958b749e7855255c860247f98da6/images/11-5.png -------------------------------------------------------------------------------- /images/11-6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kila097/CS246_Notes/b34c1597a5e5958b749e7855255c860247f98da6/images/11-6.png -------------------------------------------------------------------------------- /images/4-1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kila097/CS246_Notes/b34c1597a5e5958b749e7855255c860247f98da6/images/4-1.jpg -------------------------------------------------------------------------------- /images/4-2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kila097/CS246_Notes/b34c1597a5e5958b749e7855255c860247f98da6/images/4-2.jpg -------------------------------------------------------------------------------- /images/6-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kila097/CS246_Notes/b34c1597a5e5958b749e7855255c860247f98da6/images/6-1.png -------------------------------------------------------------------------------- /images/6-10.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kila097/CS246_Notes/b34c1597a5e5958b749e7855255c860247f98da6/images/6-10.png -------------------------------------------------------------------------------- /images/6-11.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kila097/CS246_Notes/b34c1597a5e5958b749e7855255c860247f98da6/images/6-11.png -------------------------------------------------------------------------------- /images/6-12.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kila097/CS246_Notes/b34c1597a5e5958b749e7855255c860247f98da6/images/6-12.png -------------------------------------------------------------------------------- /images/6-12b.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kila097/CS246_Notes/b34c1597a5e5958b749e7855255c860247f98da6/images/6-12b.png -------------------------------------------------------------------------------- /images/6-13.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kila097/CS246_Notes/b34c1597a5e5958b749e7855255c860247f98da6/images/6-13.png -------------------------------------------------------------------------------- /images/6-14.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kila097/CS246_Notes/b34c1597a5e5958b749e7855255c860247f98da6/images/6-14.png -------------------------------------------------------------------------------- /images/6-15.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kila097/CS246_Notes/b34c1597a5e5958b749e7855255c860247f98da6/images/6-15.png -------------------------------------------------------------------------------- /images/6-16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kila097/CS246_Notes/b34c1597a5e5958b749e7855255c860247f98da6/images/6-16.png -------------------------------------------------------------------------------- /images/6-17.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kila097/CS246_Notes/b34c1597a5e5958b749e7855255c860247f98da6/images/6-17.png -------------------------------------------------------------------------------- /images/6-18.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kila097/CS246_Notes/b34c1597a5e5958b749e7855255c860247f98da6/images/6-18.png -------------------------------------------------------------------------------- /images/6-19.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kila097/CS246_Notes/b34c1597a5e5958b749e7855255c860247f98da6/images/6-19.png -------------------------------------------------------------------------------- /images/6-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kila097/CS246_Notes/b34c1597a5e5958b749e7855255c860247f98da6/images/6-2.png -------------------------------------------------------------------------------- /images/6-20.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kila097/CS246_Notes/b34c1597a5e5958b749e7855255c860247f98da6/images/6-20.png -------------------------------------------------------------------------------- /images/6-3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kila097/CS246_Notes/b34c1597a5e5958b749e7855255c860247f98da6/images/6-3.png -------------------------------------------------------------------------------- /images/6-4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kila097/CS246_Notes/b34c1597a5e5958b749e7855255c860247f98da6/images/6-4.png -------------------------------------------------------------------------------- /images/6-5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kila097/CS246_Notes/b34c1597a5e5958b749e7855255c860247f98da6/images/6-5.png -------------------------------------------------------------------------------- /images/6-6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kila097/CS246_Notes/b34c1597a5e5958b749e7855255c860247f98da6/images/6-6.png -------------------------------------------------------------------------------- /images/6-7.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kila097/CS246_Notes/b34c1597a5e5958b749e7855255c860247f98da6/images/6-7.png -------------------------------------------------------------------------------- /images/6-8.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kila097/CS246_Notes/b34c1597a5e5958b749e7855255c860247f98da6/images/6-8.png -------------------------------------------------------------------------------- /images/6-9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kila097/CS246_Notes/b34c1597a5e5958b749e7855255c860247f98da6/images/6-9.png -------------------------------------------------------------------------------- /images/9-1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kila097/CS246_Notes/b34c1597a5e5958b749e7855255c860247f98da6/images/9-1.jpg -------------------------------------------------------------------------------- /images/9-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kila097/CS246_Notes/b34c1597a5e5958b749e7855255c860247f98da6/images/9-1.png -------------------------------------------------------------------------------- /images/9-10.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kila097/CS246_Notes/b34c1597a5e5958b749e7855255c860247f98da6/images/9-10.jpg -------------------------------------------------------------------------------- /images/9-11.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kila097/CS246_Notes/b34c1597a5e5958b749e7855255c860247f98da6/images/9-11.jpg -------------------------------------------------------------------------------- /images/9-12.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kila097/CS246_Notes/b34c1597a5e5958b749e7855255c860247f98da6/images/9-12.jpg -------------------------------------------------------------------------------- /images/9-13.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kila097/CS246_Notes/b34c1597a5e5958b749e7855255c860247f98da6/images/9-13.jpg -------------------------------------------------------------------------------- /images/9-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kila097/CS246_Notes/b34c1597a5e5958b749e7855255c860247f98da6/images/9-2.png -------------------------------------------------------------------------------- /images/9-3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kila097/CS246_Notes/b34c1597a5e5958b749e7855255c860247f98da6/images/9-3.png -------------------------------------------------------------------------------- /images/9-4.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kila097/CS246_Notes/b34c1597a5e5958b749e7855255c860247f98da6/images/9-4.jpg -------------------------------------------------------------------------------- /images/9-4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kila097/CS246_Notes/b34c1597a5e5958b749e7855255c860247f98da6/images/9-4.png -------------------------------------------------------------------------------- /images/9-5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kila097/CS246_Notes/b34c1597a5e5958b749e7855255c860247f98da6/images/9-5.png -------------------------------------------------------------------------------- /images/9-6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kila097/CS246_Notes/b34c1597a5e5958b749e7855255c860247f98da6/images/9-6.png -------------------------------------------------------------------------------- /images/9-7.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kila097/CS246_Notes/b34c1597a5e5958b749e7855255c860247f98da6/images/9-7.jpg -------------------------------------------------------------------------------- /images/9-8.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kila097/CS246_Notes/b34c1597a5e5958b749e7855255c860247f98da6/images/9-8.png -------------------------------------------------------------------------------- /images/9-9.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kila097/CS246_Notes/b34c1597a5e5958b749e7855255c860247f98da6/images/9-9.jpg --------------------------------------------------------------------------------