|
| 1 | +# C++ Tutorial |
| 2 | + |
| 3 | +Maintainers: |
| 4 | +* Radu Nichita (radunichita99@gmail.com) |
| 5 | +* Darius Neatu (neatudarius@gmail.com) |
| 6 | + |
| 7 | +## TLDR |
| 8 | +A short summary for what you need / should use at PA can be found at [PA C++: TLDR](tldr.md). |
| 9 | + |
| 10 | +## Docs |
| 11 | + |
| 12 | +Links: |
| 13 | +* [en.cppreference.com](https://en.cppreference.com): main C++ documentation |
| 14 | + * [C++ reference](https://en.cppreference.com/w/cpp): C++ documentation |
| 15 | + * [cppreference/algorithm](https://en.cppreference.com/w/cpp/algorithm): STL algorithms docs |
| 16 | + * [cppreference/container](https://en.cppreference.com/w/cpp/container): STL containers docs |
| 17 | +* [C reference](https://en.cppreference.com/w/c): C API also available in C++ |
| 18 | + |
| 19 | +C++ has multiple standards (see [cppreference/history](https://en.cppreference.com/w/cpp/language/history)). In this tutorial the C++17 standard (g++ flag: `-std=c++17`) is assumed. The laboratory skeletons use C++17 and we strongly encourage to also do it for homeworks / projects. |
| 20 | + |
| 21 | +## C++ basics |
| 22 | + |
| 23 | +* C++ is a high-level language, sometimes called a superset of C. |
| 24 | +* It supports imperative, object-oriented and generic programming. |
| 25 | + |
| 26 | +### Hello Gigel in C++ |
| 27 | + |
| 28 | +```cpp |
| 29 | +#include <iostream> |
| 30 | + |
| 31 | +int main() { |
| 32 | + int x; |
| 33 | + std::cin >> x; |
| 34 | + std::cout << "Hello, Gigel! I got " << x << " from the PA team!" |
| 35 | + return 0; |
| 36 | +} |
| 37 | +``` |
| 38 | + |
| 39 | +Explanations: |
| 40 | +* `io`: |
| 41 | + * The standard C++ input/output library is `<iostream>`. |
| 42 | + * Streams: |
| 43 | + * `std::cin`: reading from `STDIN` with operator `>>` |
| 44 | + * `std::cout`: writing to `STDOUT` with operator `<<` |
| 45 | + * `std::cerr`: writing to `STDERR` with operator `<<` |
| 46 | + * Please check `io.md` for file operations. |
| 47 | +* `namespace` |
| 48 | + * >Namespaces provide a method for preventing name conflicts in large projects. ([cppreference/namespace](https://en.cppreference.com/w/cpp/language/namespace)) |
| 49 | + * A namespace groups variables, types and functions together. |
| 50 | + |
| 51 | + |
| 52 | +* To compile any C++ program, use |
| 53 | +```bash |
| 54 | +# compile |
| 55 | +g++ -Wall -Wextra -std=c++17 <source_name> -o <executable_name> |
| 56 | +``` |
| 57 | + |
| 58 | +* To run any compiled C++ program, use |
| 59 | +```bash |
| 60 | +./<executable_name> |
| 61 | +``` |
| 62 | + |
| 63 | +--- |
| 64 | +Notes for `PA-only`: |
| 65 | + * Compiler flags: |
| 66 | + * always use `-std=c++17` |
| 67 | + * never use optimizations flags (the purpose of this class is to find the solution with the best complexity) |
| 68 | + * It's `encouraged` to use `using namespace std` ([stackoverflow/using-namespace-std](https://stackoverflow.com/questions/1452721/why-is-using-namespace-std-considered-bad-practice)) instead of specifing `std::` everywhere. This tutorial we'll use the second approach. |
| 69 | + * Always use the provided skeleton for the laboratory. |
| 70 | + |
| 71 | +--- |
| 72 | + |
| 73 | + |
| 74 | +### C vs C++ |
| 75 | +* **struct in C** vs **struct in C++** |
| 76 | + * > "A struct in the C programming language is a composite data type declaration that defines a physically grouped list of variables under one name in a block of memory, allowing the different variables to be accessed via a single pointer or by the struct declared name which returns the same address. The struct data type can contain other data types so is used for mixed-data-type records such as a hard-drive directory entry (file length, name, extension, physical address, etc.), or other mixed-type records (name, address, telephone, balance, etc.)." ([wikipedia/struct (C programming language)](https://en.wikipedia.org/wiki/Struct_(C_programming_language))) |
| 77 | + * > "A class in C++ is a user-defined type or data structure declared with keywords struct or class that has data and functions (also called member variables and member functions or methods) as its members whose access is governed by the three access specifiers private, protected or public." ([wikpedia/C++ classes](https://en.wikipedia.org/wiki/C%2B%2B_classes)). |
| 78 | +
|
| 79 | + |
| 80 | +* Basic example of C struct conversion to C++ struct |
| 81 | + |
| 82 | +In C we can define a struct and some functions which can process this type. |
| 83 | + |
| 84 | +```cpp |
| 85 | +// C struct |
| 86 | +typedef struct { |
| 87 | + double re; |
| 88 | + double im; |
| 89 | +} complex_t; |
| 90 | + |
| 91 | +void print_complex(complet_t c) |
| 92 | +{ |
| 93 | + printf("%lf + %lf\n", c.re, c.im); |
| 94 | +} |
| 95 | + |
| 96 | +int main() |
| 97 | +{ |
| 98 | + complex_t c = {1, 2}; // 1 + 2i |
| 99 | + print_complex(c); |
| 100 | + return 0; |
| 101 | +} |
| 102 | +``` |
| 103 | +
|
| 104 | +In C++ we can move the functions which process the new type inside the struct. |
| 105 | +```cpp |
| 106 | +// C++ struct |
| 107 | +struct Complex { |
| 108 | + double re, im; |
| 109 | +
|
| 110 | + void print() { |
| 111 | + // the im and re members for the current Complex object are accessed |
| 112 | + std::cout << re << "+" << im << "i" << "\n"; |
| 113 | + } |
| 114 | +}; |
| 115 | +
|
| 116 | +int main() |
| 117 | +{ |
| 118 | + Complex c; |
| 119 | + c.re = 1; |
| 120 | + c.im = 2; |
| 121 | + c.print(); |
| 122 | + return 0; |
| 123 | +} |
| 124 | +``` |
| 125 | + |
| 126 | +* **access specifiers**: struct vs class in C++ |
| 127 | + |
| 128 | + Members (data and methods) can be: |
| 129 | + * **public** - accessible from anywhere |
| 130 | + * **private** - only accessible in the class |
| 131 | + * **protected** - only accessible on the inheritance chain (not relevant for PA) |
| 132 | + |
| 133 | + Notes: |
| 134 | + * **struct** members are **public** by default. |
| 135 | + * **class** members are **private** by default. |
| 136 | + |
| 137 | +```cpp |
| 138 | +#include <iostream> |
| 139 | + |
| 140 | +class Gigel { |
| 141 | + int x = 100; // private because is in a class; |
| 142 | + // if Gigel was struct, x was public. |
| 143 | +public: |
| 144 | + void show() { |
| 145 | + std::cout << "x is " << x << "\n"; |
| 146 | + } |
| 147 | +}; |
| 148 | + |
| 149 | +int main() { |
| 150 | + Gigel g; |
| 151 | + g.show(); // print "x is 100" |
| 152 | + return 0; |
| 153 | +} |
| 154 | +``` |
| 155 | +
|
| 156 | +### Constructors and destructors |
| 157 | +* [cppreference/constructor](https://en.cppreference.com/w/cpp/language/constructor) |
| 158 | +* [cppreference/destructor](https://en.cppreference.com/w/cpp/language/destructor) |
| 159 | +
|
| 160 | +#### Constructor |
| 161 | +A constructor is **a special method** that is **automatically** called `when` an object of a class is created. |
| 162 | +* The constructor has the same name as the class name. |
| 163 | +* The constructor has **no return type**. |
| 164 | +* The constructor can take 0 or more parameters (just like regular functions). |
| 165 | +* If no constructor is defined, the compiler "creates" one, which does nothing and it is called **default constructor**. |
| 166 | +* A class can have **multiple** constructors with different parameters. |
| 167 | +
|
| 168 | +```cpp |
| 169 | +#include <iostream> |
| 170 | +
|
| 171 | +class Gigel { |
| 172 | + public: |
| 173 | + Gigel() { |
| 174 | + std::cout << "Gigel()\n"; |
| 175 | + } |
| 176 | +
|
| 177 | + Gigel(int x) { |
| 178 | + std::cout << "Gigel(" << x << ")\n"; |
| 179 | + } |
| 180 | +}; |
| 181 | +
|
| 182 | +int main() { |
| 183 | + Gigel g; // print "Gigel()" |
| 184 | + Gigel g2{}; // print "Gigel()" |
| 185 | + Gigel g3{10}; // print "Gigel(10)" |
| 186 | + Gigel g4(5); // print "Gigel(5)" |
| 187 | +} |
| 188 | +``` |
| 189 | +Note: In C++ we call the constructor for a class `Gigel` by using `Gigel()` or `Gigel{}`. It's strongly recommended to only use the `{}` notation in order to avoid some weird compile error (see [Most vexing parse C++](https://en.wikipedia.org/wiki/Most_vexing_parse)). |
| 190 | + |
| 191 | +#### Destructor |
| 192 | +A destructor is a **special method** that is called `when` the lifetime of an object ends. The purpose |
| 193 | +of the destructor is to free the resources that the object may have acquired during its lifetime. |
| 194 | + |
| 195 | +### Dynamic allocation: C vs C++ |
| 196 | +* **new / malloc** are used to dynamically allocate memory. A difference is that **new** does call the |
| 197 | +constructor and **malloc** does not. |
| 198 | + |
| 199 | +* **delete / free** are used to free the allocate memory. A difference is that **delete** does call the |
| 200 | +destructor and **free** does not. |
| 201 | + |
| 202 | +Note: The usage of malloc / free in C++ is considered unsafe and it's forbidden. The new / delete functions should be used instead. |
| 203 | + |
| 204 | +--- |
| 205 | + |
| 206 | +During the PA labs you won't used dynamic allocation, because we want to keep the things as simple as possible: |
| 207 | +* a single call to `new / delete` will be made in the `main()` function - the skeleton already has the implementation for `main()`. |
| 208 | +* when solving the tasks you must use containers from STL (e.g. `std::vector`) which allocate and automatically deallocate memory. |
| 209 | + |
| 210 | +--- |
| 211 | + |
| 212 | +### auto keyword |
| 213 | +* > "For variables, specifies that the type of the variable that is being declared will be automatically deduced from its initializer. For functions, specifies that the return type will be deduced from its return statements." ([cppreference/auto](https://en.cppreference.com/w/cpp/language/auto)) |
| 214 | +
|
| 215 | +```cpp |
| 216 | +#include <iostream> |
| 217 | + |
| 218 | +// the return type for square() is automaticaly deduce to double because the returned value |
| 219 | +// (x * x) is a double |
| 220 | +auto square(double x) { |
| 221 | + return x * x; |
| 222 | +} |
| 223 | + |
| 224 | +int main() { |
| 225 | + auto x = 10.0; // the type of x is automatically deduced to double because 10.0 it's a double |
| 226 | + std::cout << square(x) << "\n"; |
| 227 | + return 0; |
| 228 | +} |
| 229 | +``` |
| 230 | +
|
| 231 | +### References in C++ |
| 232 | +* A pointer is a **variable** that holds a memory address (e.g. `0xCAFEBABE` or the address of another variable). |
| 233 | +* Both C and C++ have pointers. Additionally, C++ has references. |
| 234 | +* A reference is **an alias** for **an already existing variable**. Once a reference is initialized to a variable, it cannot be changed to refer to another variable (hence, a reference is similar to a fixed pointer). The referenced variable can be changed using the reference. |
| 235 | +
|
| 236 | +```cpp |
| 237 | +#include <iostream> |
| 238 | +
|
| 239 | +// C: if we want to change x in function foo, we need to pass using a pointer |
| 240 | +void modify_c(int *x) { |
| 241 | + *x = 123; |
| 242 | +} |
| 243 | +// call example: |
| 244 | +// int x; |
| 245 | +// modify_c(&x); |
| 246 | +
|
| 247 | +// C++: we can pass by reference |
| 248 | +void modify_cpp(int &x) { |
| 249 | + x = 123; // x it's a reference to the "x" variable from main |
| 250 | +} |
| 251 | +
|
| 252 | +int main() { |
| 253 | + int x = 0; |
| 254 | + modify_cpp(x); |
| 255 | + std::cout << x << "\n"; // print 123 because x was changed to 123 in modify_cpp() |
| 256 | + return 0; |
| 257 | +} |
| 258 | +``` |
| 259 | + |
| 260 | +### Templates |
| 261 | +In C++, templates allow to pass to functions/classes the data type as parameter, so there is no need to write the same code for different data types. |
| 262 | + |
| 263 | +#### template functions |
| 264 | +A function which may have generic parameter(s) or return type. |
| 265 | +* generic return type |
| 266 | + |
| 267 | +```cpp |
| 268 | +template<typename T> |
| 269 | +T func(...) { |
| 270 | + // ... |
| 271 | + return T{...}; |
| 272 | +} |
| 273 | +// usage example for int |
| 274 | +int x = func<int>(); |
| 275 | + |
| 276 | +// usage example for struct Gigel |
| 277 | +Gigel gigel = func<Gigel>(); |
| 278 | +// or |
| 279 | +auto gigel = func<Gigel>(); |
| 280 | +``` |
| 281 | +
|
| 282 | +* generic parameter type(s) |
| 283 | +```cpp |
| 284 | +// example of template function |
| 285 | +// which has 2 parameters of type T (reference to T) |
| 286 | +template<typename T> |
| 287 | +void func(T& a, T&b) { |
| 288 | + // ... |
| 289 | +} |
| 290 | +// usage example for int |
| 291 | +func<int>(123, 456); |
| 292 | +// example of template function |
| 293 | +// which has 2 parameters of type K and V |
| 294 | +template<typename K, typename B> |
| 295 | +void func(K &a, V &v) { |
| 296 | + // ... |
| 297 | +} |
| 298 | +// usage example for int and struct Gigel |
| 299 | +func<int, Gigel>(123, Gigel{}); |
| 300 | +``` |
| 301 | + |
| 302 | +```cpp |
| 303 | +#include <iostream> |
| 304 | + |
| 305 | +// generic maximum function: a and b have the same template type T |
| 306 | +template <typename T> |
| 307 | +T my_max (T a, T b) { |
| 308 | + return (a > b ? a : b); |
| 309 | +} |
| 310 | + |
| 311 | +int main() { |
| 312 | + // usage example for T = int |
| 313 | + std::cout << my_max<int>(2, 3) << "\n"; |
| 314 | + // or let to compile to automatically determine the T type from the passed values |
| 315 | + std::cout << my_max(2, 3) << "\n"; |
| 316 | + |
| 317 | + // usage example for T = double |
| 318 | + std::cout << my_max(3.4, 5.6) << "\n"; |
| 319 | + |
| 320 | + // usage example for T = char |
| 321 | + std::cout << my_max('a', 'b') << "\n"; |
| 322 | + return 0; |
| 323 | +} |
| 324 | +``` |
| 325 | +
|
| 326 | +### template classes |
| 327 | +Classes which depend of a template type `T` (e.g. a struct with a `T` member which can be an `int` or ` double`). |
| 328 | +Consider the next simple class which has an `int` member; |
| 329 | +
|
| 330 | +```cpp |
| 331 | +class MyClass { |
| 332 | +public: |
| 333 | + int get_member() { return member; } |
| 334 | + void set_member(int new_value) { member = new_value; } |
| 335 | +private: |
| 336 | + int member; |
| 337 | +}; |
| 338 | +``` |
| 339 | + |
| 340 | +If we want to make a similar class with a member `double` we would copy the class and replace `int` with `double`. We also need to rename the class. If instead we use a template class, we need to: |
| 341 | + |
| 342 | +* replace `int` with `T` (a generic type) |
| 343 | +* declare that the class is template (using ```cpp template<typename T>``` before class MyClass) |
| 344 | + |
| 345 | +```cpp |
| 346 | +template<typename T> |
| 347 | +class MyClass { |
| 348 | +public: |
| 349 | + T get_member() { return member; } |
| 350 | + void set_member(T new_value) { member = new_value; } |
| 351 | +private: |
| 352 | + T member; |
| 353 | +}; |
| 354 | + |
| 355 | +// usage example for int |
| 356 | +MyClass<int> mc_int; |
| 357 | +mc_int.set_member(123); // set an int value |
| 358 | +std::cout << mc_int.get_member() << "\n"; // print 123 |
| 359 | + |
| 360 | +// usage example for class Gigel |
| 361 | +MyClass<Gigel> mc_gigel; |
| 362 | +mc_gigel.set_member(Gigel{}); // set a Gigel value |
| 363 | +std::cout << mc_gigel.get_member() << "\n"; // print the Gigel value |
| 364 | +``` |
| 365 | +
|
| 366 | +## STL |
| 367 | +C++ provides implementation for various data types and algorithms which are grouped into the `STL` (the Standard Template Library - generic / template functions and classes). |
| 368 | +
|
| 369 | +Please continue the tutorial from the `STL` tree. |
0 commit comments