# C++ ## Class ### Concepts - Encapsulation(封装) properties, methods `private` keyword hides some details that users don't need to know ```cpp= struct Point { double x; double y; }; using namespace std; int main() { Point points[10]; for (int i = 0; i != 10; ++i) { cout << points[i].x << ' ' << points[i].y << endl; } double xs[10]; double ys[10]; for (int i = 0; i != 10; ++i) { cout << xs[i] << ' ' << ys[i] << endl; } } ``` - Inheritance(继承) class A inherits class B if A has all properties and methods of B. - Polymorphism (多态) Same method, different subclasses, different behaviors. ## Delcaration & Definition ```cpp= /// declaration: tells compiler there exists a xxx class Point; // class declaration int add(int a, int b); // function declaration ``` ```cpp= /// Definition of class // same as struct definition struct Point { // properties or members int x, y; }; class Car { // properties int max_speed; // ... // methods or member functions void run(); void stop(); }; class Dog { int color; int sort; // ... void bark(); void run(); void eat(); // ... }; ``` ## Constructor and Destructor used to initiate and finalize an object. - Class: an **abstract set** of things that share the same properties and methods. (Ex. ```struct Point { int x, y; }```) - Object / Instance: a specific element of the class, i.e., holding specific(distinct) properties and behaviors. (Ex. ``` Point(1, 2), Point(-3, 0)```) ```cpp= class Vector { // class public: // for now, must int x; int y; // Zero parameter constructor Vector() { x = y = 0; } Vector(int c) { x = y = c; } Vector(int a, int b) { x = a; y = b; } // Destructor, doesn't accept parameter ~Vector() { cout << "Destructing (" << x << ", " << y << ")..."; } // Copy constructor, clone Vector(const Vector& v) { x = v.x; y = v.y; } }; // int a{2}; Vector v{1, 2}; // object, instance(实例) Vector u{-3, 0}; Vector v1{}; // calls zero parameter constructor, v1 = {x=0, y=0} Vector v2{2}; // v2 = {2, 2} Vector v3{v2}; // call copy constructor // v3 === v2 cout << v.x << u.y; // 10 // Destruting v3(2, 2)... // Destruting v2(2, 2)... // Destruting v1(0, 0)... // Destruting u(-3, 0)... // Destruting v(1, 2)... ``` **Big Three (the rule of three)**: If one of constructor / destructor / copy constructor is defined, then all of three need to be defined. ```cpp= class Box { private: int length, width, height; // properties int area; const int dimension; // const member public: int compute_area() { // method: inner-class function, member function // maybe complex and time-consuming int lxw = length * width; int wxh = width * height; int hxl = height * length; int area = 2 * (lxw + wxh + hxl); return area; } // colon syntax Box(int l, int w, int h) : length{l}, width{w}, height{h} , dimension{3} { /* generally, we put direct assignment of properties after colon * length = l; * width = w; * height = h; */ area = compute_area(); /* const initialization must be put after colon * dimension = 3; // wrong! */ } // Delegating constructor, i.e., calling another constructor // Delegating constructor must be placed after colon // int h=1 is default parameter for h Box(int l, int h=1) : Box{l, l, h} { } /* above expands to two constructors * (1) Box(int l, int h) : Box{l, l, h} {} * (2) Box(int l) : Box {l, l, 1} {} */ }; int main() { Box b1{1, 2, 3}; // call L.18, get (1, 2, 3) Box b2{1, 2}; // call L.37, get (1, 1, 2) Box b3{1}; // call L.38, get (1, 1, 1) } ``` ## Visibility `public` vs `private`(default) ```cpp= class Vector { // class int x; // default private int y; public: // Constructor must be public Vector(int a, int b) { x = a; y = b; } // definition of method double length() { return sqrt(length_square()); // length_square is callable, because the caller is inside the class. } private: double length_square() { // defined as private member function return x*x + y*y; } }; int main() { Vector v{3, 4}; // cout << v.x; // error, v.x is private // cout << v.length_square() // error, length_square() is private cout << v.length(); // OK, 5 } ``` What should be `private`? - **can be accessed in the class, but cannot be visited outside the class** - maybe the property is too complicated to users, the user need not to know about it - e.g. `fire_the_engine()` in `Car` class is too complicated for users. - Users only need to know `drive()` - maybe the property is concealed to users, the user cannot know about it - e.g. `exam_score` in `Student` class needs concealing. - Otherwise we can visit his/her score without permission of the student. - maybe the property that can be modified by users but with restrictions - e.g. `max_speed` in `Car` class should not be modified by users. What should be `public`? - **can be accessed from everywhere** - constructors & destructors - maybe the property is convenient to users for modifications ## Methods ```cpp= class Vector { // class public: // for now, must int x; int y; // definition of method double length() { return sqrt(x*x + y*y); } // method with parameter Vector shift(const Vector& v) { return Vector{x+v.x, y+v.y}; } }; int main() { Vector v{3, 4}; cout << v.length(); // 5 cout << v.shift(v).length(); // 10 } ``` ### Destructors & Copy Constructor #### Correct version ```cpp= // used to store a set of objects with unknown number. class Set { int* elems; // the start address of the storage int size; // the size of the storage public: Set(int size_) : size{size_} { elems = new int[size]; // equivalent to: // elems = malloc(size * sizeof(int)) } // destructor, used to clean space in the constructor ~Set() { size_ = 0; delete[] elems; elems = nullptr; // equivalent to elems = (void*) 0; } // copy constructor Set(const Set& set) { size = set.size; /* * elems and set.elems are the same * so elems[i] == set.elems[i] * and get(i) == set.get(i) * even though set.set(i, 0) */ // elems = set.elems; // Wrong elems = new int[size]; // consider elems[i]? At present, it is not equal to set.elems[i] for (int i = 0; i != size; ++i) { elems[i] = set.elems[i]; } } int get(int i) { if (i >= 0 && i < size) return elems[i]; } void set(int i, int value) { if (i >= 0 && i < size) elems[i] = value; } }; int main() { Set S{10}; // S contains 10 elements for (int i = 0; i != 10; ++i) { S.set(i, i*i); } // S = {0, 1, 4, 9, 16, 25, 36, 49, 64, 81} // At the end of main(), S will be destroyed. // The destructor of S will be invoked. // If S is not properly handled, there would be memory leak. Set T{S}; // call copy constructor // T = {0, 1, 4, 9, 16, 25, 36, 49, 64, 81} T.set(0, 100); // T.elems[0] = 100 // S = {0, 1, 4, 9, 16, 25, 36, 49, 64, 81} // T = {100, 1, 4, 9, 16, 25, 36, 49, 64, 81} cout << S.get(0) << T.get(0); // 0, 100 } ``` #### Wrong version ```cpp= class Set { int* elems; // the start address of the storage int size; // the size of the storage public: Set(int size_) : size{size_} { elems = new int[size]; // equivalent to: // elems = malloc(size * sizeof(int)) } // destructor, used to clean space in the constructor ~Set() { delete[] elems; elems = nullptr; // equivalent to elems = (void*) 0; } // copy constructor Set(const Set& set) { size = set.size; elems = set.elems; // so elems[i] = set.elems[i] } int get(int i) { if (i >= 0 && i < size) return elems[i]; } void set(int i, int value) { if (i >= 0 && i < size) elems[i] = value; } }; int main() { Set S{10}; // S contains 10 elements for (int i = 0; i != 10; ++i) { S.set(i, i*i); } // S.elems = {0, 1, 4, 9, 16, 25, 36, 49, 64, 81} Set T{S}; // call copy constructor // T.elems = S.elems = {0, 1, 4, 9, 16, 25, 36, 49, 64, 81} T.set(0, 100); // T.elems = S.elems = {100, 1, 4, 9, 16, 25, 36, 49, 64, 81} cout << S.get(0) << T.get(0); // 100, 100 } ``` Compare two copy constructors ```cpp= // From the right version Set(const Set& set) { size = set.size; elems = new int[size]; for (int i = 0; i != size; ++i) { elems[i] = set.elems[i]; } } // From the wrong version Set(const Set& set) { size = set.size; elems = set.elems; // so elems[i] = set.elems[i] } ``` :::info Exercise: Implement a `List` class Example: List: Node(1) -> Node(2) -> Node(3) -> nullptr ```cpp= struct Node { int value; Node* next; // store address of the next node }; class List { int size_; // size of list Node* head; // address of the first node // constructor List() { size_ = 0; head = nullptr; } // copy constructor List(const List& list) { // WRONG WAY, same as Set, two lists share the same space // size_ = list.size_; // head = list.head; // namely copy every node, totally list.size() nodes // list: Node(0) -> Node(1) -> ... // Step 0. initialize size_ and head size_ = 0; head = nullptr; // Step 1. copy Node(0), how? // Node(0) // Step 2. copy Node(1) // Node(1) -> Node(0), not expected // Expected: Node(0) -> Node(1) // ... // Step LAST. copy Node(list.size()-1) /* prepend(list.get(list.size()-1)); ... prepend(list.get(1)); prepend(list.get(0)); */ for (int i = list.size()-1; i >= 0; --i) { prepend(list.get(i)); } // At present, // Node(0) -> Node(1) -> ... -> Node(list.size()-1) // size_ = list.size_ // head points to Node(0) // because after prepend(value), head points to Node(value) // At last prepend, i.e. prepend(0), head points to Node(0) } // destructor ~List() { size_ = 0; // delete[] head; // delete[] only pointer created by new Type[] Node* cur = head; // delete current Node Node* next; // check next Node while (cur != nullptr) { // if current node is not nullptr next = cur->next; // record next node to be checked delete cur; // delete current node cur = next; // will check next on the next iteration } head = nullptr; } // return size of list int size() const { return size_; } // setter, set the last node // Node(2) -> Node(3) -> nullptr // after prepend(1) // Node(1) -> Node(2) -> Node(3) -> nullptr void prepend(int value) { /* Node(0) -> Node(1) -> Node(2) -> ... head ↑ */ // step 1: create a new node: Node(value) /* Node(0) -> Node(1) -> Node(2) -> ... head ↑ Node(value)->?which node which node: Node(0), which head points to */ Node* node = new Node{value, head}; /* Node(value) -> Node(0) -> Node(1) -> Node(2) -> ... head↑ */ // step 2: move head to new node head = node; /* Expected: Node(value) -> Node(0) -> Node(1) -> Node(2) -> ... head ↑ */ ++size_; } // read the index-th element // get(0), equivalent to head->value // get(1), (head->next)->value // get(2), (head->next->next)->value // get(i), get(i-1)->next->value int get(int index) const { /* // (1) recursion if (index == 0) { return head->value; } else { return getNode(index-1)->next->value; } */ // (2) iteration // we know get(i) needs taking next i times node* cur = head; // for loop: taking index next operations for (int i = 1; i <= index; ++i) { cur = cur->next; // Now, cur directs to the i-th node } /* * cur = head * head=Node(0) -> Node(1) -> Node(2) -> ... -> Node(index) * cur ↑ * i = 1 * cur = cur->next = head->next * head=Node(0) -> Node(1) -> Node(2) -> ... -> Node(index) * cur ↑ * i = 2 * cur = cur->next = head->next->next * head=Node(0) -> Node(1) -> Node(2) -> ... -> Node(index) * cur ↑ * ... * i = index * cur = cur->next = head->(index times next) * head=Node(0) -> Node(1) -> Node(2) -> ... -> Node(index) * cur ↑ */ // Now, cur points to the index-th node return cur->value; } }; int main() { List list; // create a List object std::cout << list.size(); // 0 list.prepend(3); // 3 list.prepend(10); // 10 -> 3 list.prepend(-8); // -8 -> 10 -> 3 // copy constructor List list2{list}; list2.prepend(5); // 5 -> -8 -> 10 -> 3 std::cout << list.size(); // 3 std::cout << list2.get(2); // 10 } ``` ::: ### new & delete keyword ```cpp= int* x = new int{1}; // *x = 1 // equivalent to // int* x = malloc(sizeof(int)); // *x = 1; delete x; // equivalent to free(x) x = nullptr; int* arr = new int[5]; // equivalent to // int* arr = malloc(sizeof(int) * 5); delete[] arr; // equivalent to free(arr) arr = nullptr; // nullptr = (void*) 0 ``` ### Const keyword `const` is used to decorate a member function. There are **three** different usages. - type 1: decorate on parameters - type 2: decorate on return type - type 3: decorate on member function itself **Const member function**: `const` decorated at the end of member function ```cpp= class Integer { // wraps a plain int int value; // member public: // plus method void plus(/*type (1)*/ const Integer &integer) { value += integer.value; // const means one cannot modify the argument // integer.value = 1; // prohibited } void plus2(Integer &integer) const { // const member function // value += integer.value // Wrong integer.value = 1; // OK } void plus3(const Integer &integer) const { // const member function // value += integer.value // Wrong // integer.value = 1 // Wrong } // return next integer /*type (2)*/ const Integer next() { return Integer(value + 1); } // const means always returns a const object // convert to int int to_int() const /*type (3)*/ { return value; // const means the member function cannot modify members // value = 1; // prohibited, it affects `value` // not even call other non-const member function // plus(Integer(1)); // also prohibited // // next(); // also prohibited // next() doesn't modify any member, // BUT next() is not const member function // ! const member function only allows to call *other* const member function } }; int main() { Integer five(5), eight(8); five.plus(eight); // five = 13 // Integer nine = eight.next(); // Wrong, type (2) const // Left hand side is a non-const object: nine // Right hand side is const object: eight.next() const Integer nine = eight.next(); // Correct } ``` :::info Exercise: Implement an optional int class. ```cpp= class OptionalInt { // properties int* value; public: bool has_value() const { // return hasvalue == true; return value != nullptr; } int value_or(int val) const { if (has_value()) { return *value; } else { return val; } } void clear() { if (has_value()) { delete value; } value = nullptr; } void set_value(int val) { *value = val; } OptionalInt(int val) { value = new int{val}; // `new` allocates a fresh space on memory // `int` indicates the space contains an int // `{val}` specifies the value contained in the space // `=` assigns address of the space to `value` } OptionalInt() { value = nullptr; } ~OptionalInt() { clear(); } }; int main() { OptionalInt x{5}; // x = 5 OptionalInt y{}; // y has no value x.has_value(); // true y.has_value(); // false x.value_or(3); // x has value, returns 5 y.value_or(3); // y has no value, returns 3 x.clear(); // x has no value y.set_value(7) // y = 7 } ``` ::: ### this pointer `this` pointer is used in class to indicate current instance. Usually used to avoid ambiguity. **`this` pointer always refers to member property / method.** ```cpp= class C { int x; public: C(int x) { // x = x; // ambiguity this->x = x; // LHS: this->x is member x, refers to LN 2 // RHS: x is argument x, refers to LN 5 } void bar() { this->x = 1; // equivalent to x = 1 this->foo(1); // equivalent foo(1) } int foo(int x) { // baidu "foobar" return x; // ambiguity, return argument x, refers to LN 17 return this->x; // return member x, refers to LN 2 } void baz(int foo) { // x = foo(foo); // ambiguity // which foo: method foo() or argument foo? x = this->foo(foo); // outside refers to LN 17, inside refers to LN 22 } }; ``` ## Homework: The Magician Spellbook Catalog ```= The Magician Spellbook Catalog Problem Statement The great magic university of Fakebills has been adding a lot of new spellbooks to their library lately. The school administration has hired you to create a spellbook catalog program that will make searching for spellbooks easier. To simplify your task, the school has provided you with their spellbook information. These come in the form of a text file that file contains a list of Fakebill's spellbooks and their included spells, all of the information that your program will display. Requirements #Req 1. Command Line Argument: When starting the program, the user will provide one command line argument. The command line argument will be the name of the file that contains the information about spellbooks and included spells. If the user does not provide the name of an existing file the program should print out an error message and quit. #Req 2. Sorting and Printing: Once the program begins, the user should be prompted with a list of different ways to display the spellbook and spell information. After the user has chosen an option, they should be asked if they want the information printed to the screen or written to a file. If they choose to write to a file, they should be prompted for a file name. If the file name already exists, the information should be appended to the file. If the file name does not exist, a file should be created and the information should be written to the file. Available Options: Sort spellbooks by number of pages: If the user picks this option the books must be sorted in ascending order by number of pages. Once they are sorted, you should print/write to file the title of the book and the number of pages it has. Sort spells by effect: There are five types of spells: fire, bubble, memory_loss, death, poison. The spells with bubble as the effect should be listed first, followed by memory_loss, fire, poison, and death. Once they are sorted, you should print/write to file the spell name and its effect. Sort by average success rate of spells: You must create a list o The spellbook struct will be used to hold information from the spellbooks.txt file. This struct holds information about a spellbook. struct spellbook { string title; string author; int num_pages; int edition; int num_spells; float avg_success_rate; struct spell* s; };The spell struct will also be used to read in information from the spellbooks.txt file. This struct holds information about a spell. There are five options for effect: "fire", "poison", "bubble", "death", or "memory_loss". struct spell { string name; float success_rate; string effect; };Required Functions: You must implement the following functions in your program. You are not allowed to modify these required function declarations in any way. Note: it is acceptable if you choose to add additional functions (but you must still include the required functions). Note2: You must write the dynamic memory allocation functionality yourself (i.e. no using vectors). This function will dynamically allocate an array of spellbooks (of the requested size): spellbook* create_spellbooks(int);This function will fill a spellbook struct with information that is read in from spellbooks.txt. Hint: “ifstream &” is a reference to a filestream object. You will need to create one and pass it into this function to read from the spellbooks.txt file. void get_spellbook_data(spellbook*, int, ifstream &); This function will dynamically allocate an array of spells (of the requested size): spell* create_spells(int);This function will fill a spell struct with information that is read in from spellbooks.txt. void get_spell_data(spell*, int, ifstream &); You need to implement a function that will delete all the memory that was dynamically allocated. You can choose the prototype. A possible example prototype includes the following: void delete_spellbook_data(spellbook*, int);Required Input File Format Your program must accommodate the file formats as specified in this section. The spellbooks.txt file has the following structure. The file information will be provide ``` ### Req 1. User inputs filename, if corresponding file does not exist, print an error message and quit Knowledge point: command line IO, file IO. ```cpp= #include <iostream> // cin, cout #include <fstream> // ifstream, ofstream cin >> filename; ifstream fin; // file stream fin.open(filename, std::ios_base::in); // open file if (!fin.is_open()) { // file not exist /// print error message return -1; } ``` ### Req 2. Information Display - User inputs display option - User inputs display method (screen / file) * If user selects file display * User inputs filename * If file exist: append * Otherwise: create * Otherwise (screen): cout ```cpp= cout << "please input display option"; cin >> opt; cout << "please input display method"; cin >> method; if (method == "screen") { cout << spell_information; } else { // method == "file" cin >> filename; ofstream fout; // file output stream fout.open(filename, std::ios_base::out); if (fout.is_open()) { // file exists fout.open(filename, std::ios_base::app); // append mode } else { // create new fout = ofstream(filename, std::ios_base::out); // create and open } fout << spell_information; } ``` ### Req 3. Sort information 1. by page no. Print title / # total pages 2. by effect (F, B, M, D, P). Print spell name & effect 3. by succ rate. Create a list: Books{ Book1{spell, succ rate}}. Print title, avg succ rate 4. exit ```cpp= while (true) { bool flag{false}; // indicate whether quit while loop cin >> opt; switch (opt) { case 1: // sort by page no. spell_information = sort_by_page_no(); break; case 2: spell_information = sort_by_effect(); break; case 3: ... case 4: flag = true; break; }; if (flag) { // quit this loop break; } } // Compare method bool compare_by_page_no(spellbook const& a, spellbook const& b) { // return true if a < b return a.num_pages < b.num_pages; } int effect_type(spell const& s) { return spell.effect == "bubble" ? 1 : spell.effect == "memory_loss" ? 2 : … } bool compare_by_effect(spell const&a, spell const&b) { return effect_type(a) < effect_type(b); } string sort_by_page_no(vector<spellbook>& spellbooks) { // sort & prepare title / # total pages // Task 1. SORT by page no. // usage of std::sort in #include<algorithm> // sort(vec.begin(), vec.end(), compare_method) // compare_method(T a, T b): function returns true if a < b std::sort(spellbooks.begin(), spellbooks.end(), compare_by_page_no); // Task 2. Prepare spell info string info; for (int i = 0; i < spellbooks.length(); i++) { spellbook& book = spellbooks[i]; info += book.title + ":" + book.num_pages + "\n"; } return info; } ``` ### Required structs ```cpp= struct spell { string name; float success_rate; string effect; }; struct spellbook { string title; string author; int num_pages; int edition; int num_spells; float avg_success_rate; struct spell* s; }; ``` ### Required Functions ```cpp= // create a dynamic allocation space of given size spellbook* create_spellbooks(int size) { return new spellbook[size]; } ``` ```cpp= // read <size> spellbooks into <addr> from <fin> void get_spellbook_data(spellbook* addr, int size, ifstream& fin) { for (auto i = 0; i < size; ++i) { spellbook book; fin >> book.title >> book.author >> ...; addr[i] = book; } } ``` ```cpp= spell* create_spells(int size) { return new spell[size]; } ``` ```cpp= void get_spell_data(spell* addr, int size, ifstream& fin) { ... } ``` ```cpp= void delete_spellbook_data(spellbook* addr, int size) { // STEP 1. delete spell space for (auto i = 0; i != size; ++i) { delete[] addr[i].spell; } // STEP 2. delete spellbook space delete[] addr; } ``` ```cpp= /* main.cpp */ // STEP 1. includes #include <iostream> #include <fstream> #include <string> // STEP 2. class / struct definitions / declarations struct spell { string name; float success_rate; string effect; }; struct spellbook { string title; string author; int num_pages; int edition; int num_spells; float avg_success_rate; struct spell* s; }; // STEP 3. function definitions / declarations spellbook* create_spellbooks(int size) { return new spellbook[size]; } void get_spellbook_data(spellbook* addr, int size, ifstream& fin) { for (auto i = 0; i < size; ++i) { spellbook book; fin >> book.title >> book.author >> ...; addr[i] = book; } } spell* create_spells(int size) { return new spell[size]; } void get_spell_data(spell* addr, int size, ifstream& fin) { spell s; for (int i = 0; i != size; ++i) { fin >> s.name >> s.success_rate >> s.effect; addr[i] = s; } } void delete_spellbook_data(spellbook* addr, int size) { // STEP 1. delete spell space for (auto i = 0; i != size; ++i) { delete[] addr[i].spell; } // STEP 2. delete spellbook space delete[] addr; } bool compare_by_page_no(spellbook const& a, spellbook const& b) { // return true if a < b return a.num_pages < b.num_pages; } string sort_by_page_no(spellbooks* addr, int size) { // sort & prepare title / # total pages // Task 1. SORT by page no. // usage of std::sort in #include<algorithm> // sort(vec.begin(), vec.end(), compare_method) // compare_method(T a, T b): function returns true if a < b std::sort(addr, addr + size * sizeof spellbooks, compare_by_page_no); // Task 2. Prepare spell info string info; for (int i = 0; i < spellbooks.length(); i++) { spellbook& book = spellbooks[i]; info += book.title + ":" + book.num_pages + "\n"; } return info; } // STEP 4. main function int main() { // Req. 1 cin >> filename; ifstream fin; // file stream fin.open(filename, std::ios_base::in); // open file if (!fin.is_open()) { // file not exist /// print error message return -1; } int size; fin >> size; spellbooks* books = create_spellbooks(size); get_spellbook_data(books); for (int i = 0; i != size; ++i) { // read spells // TODO } // Req. 2 bool flag{false}; // indicate whether quit while loop while (true) { cout << "please input display option"; cin >> opt; switch (opt) { case 1: // sort by page no. spell_information = sort_by_page_no(); break; case 2: spell_information = sort_by_effect(); break; case 3: ... case 4: flag = true; break; }; cout << "please input display method"; cin >> method; if (method == "screen") { cout << spell_information; } else { // method == "file" cin >> filename; ofstream fout; // file output stream fout.open(filename, std::ios_base::out); if (fout.is_open()) { // file exists fout.open(filename, std::ios_base::app); // append mode } else { // create new fout = ofstream(filename, std::ios_base::out); // create and open } fout << spell_information; } if (flag) { // quit this loop break; } } } ``` ## Static properties and static methods ### Concept `non-static` property: each object of class has its own distinguished property value `static` property: all objects of class share the same value `non-static` method: operates on non-static or static properties. `static` method: operates ONLY static properties. ```cpp= class Dog { public: int color; // non-static property: each dog may have different color int get_color(); // non-static method void set_color(int color); // non-static method static int num_of_legs; // static property: all dogs have 4 legs static int get_num_of_legs(); // static method static void set_num_of_legs(int n); // static method }; class USDollar { public: float face_value; // non-static: each USDollar may have different face value, say $1, $2, ... static float exchange_rate; // static: all USDollars have the same exchange rate }; ``` ### Example ```cpp= class Employee { public: static inline int number_of_employees{0}; static string company; string name; int age; int gender; int department; void set_info(int age, int gender, int depart) { // non-static method this->age = age; this->gender = gender; this->department = depart; } static void set_company(string comp) { // static method // this->company = comp; // WRONG: this pointer refers to a specific object, not class company = comp; // OK Employee::company = comp; // OK // name = comp; // WRONG: operates on non-static property // Employee::name = comp; // WRONG: operates on non-static property } Employee(string name) : name{name} { number_of_employees++; } }; int main() { cout << Employee::number_of_employees; // 0 Employee Alice {"Alice"}; cout << Alice.name; // Alice cout << Alice.number_of_employees; // 1 Employee Bob {"Bob"}; cout << Bob.name; // Bob cout << Bob.number_of_employees; // 2 cout << Alice.number_of_employees; // 2 // Note: we can use classname::static_member to access the value cout << Employee::number_of_employees; // 2 Employee::set_company("Google"); // Note: we can NOT use object::member or classname::non_static_member to access // cout << Alice::name; // WRONG // cout << Bob::number_of_employees; // WRONG // cout << Employee::name; // WRONG } ``` **static class**: A class that without non-static members (properties or methods). Usually we don't (need to) create objects of static class. **static method** also applies to operations/function which needs no any member property. ```cpp= class Converter { public: static float mm_2_cm(float len) { return len / 10; } static float cm_2_mm(float len) { return len * 10; } }; int main() { cout << Converter::mm_2_cm(36); // 3.6 cout << Converter::cm_2_mm(36); // 360 Converter conv; // OK, but we usually don't do that // because we have no properties to be initialized conv.mm_2_cm(36); // OK conv.cm_2_mm(36); // OK } ``` **NOTE**: static method can NOT be const method ```cpp= class C { int x; static int y; public: int get_x() const { return x; } static int get_y() { return y; } // we can access static member by non-static method int get_y_2() const { return y; } // we cannot access non-static member by static method // static int get_x_2() { return x; } // static method cannot be const method // static int get_y_3() const { return y; } }; ``` ## File IO IO (input / output) C++ uses file stream to manage IO, similar to `cin` / `cout`. `std::ifstream` is used to read contents from file, similar `cin` reads contents from terminal `std::ofstream` is used to write contents to file, similar `cout` writes contents to the screen ```txt= =========example.txt========= 2 3 abc 1 2 3 def 4.5 6.7 8.9 ``` ```cpp= // (1) include header file #include <fstream> // contains std::ifstream, std::ofstream int main() { // Input std::ifstream fin; // create an input file stream fin.open("example.txt"); // open file named "filename.txt" // now fin behaves like cin int m, n; string names[2]; int abc[3]; double def[3]; if (fin.is_open()) { // test whether the file opens successfully // reads file fin >> m >> n; // m = 2, n = 3 fin >> names[0]; // names[0] = "abc" for (int j = 0; j < n; ++j) { fin >> abc[j]; // abc = {1, 2, 3} } fin >> names[1]; // names[1] = "def" for (int j = 0; j < n; ++j) { fin >> def[j]; // def = {4.5, 6.7, 8.9} } fin.close(); // !!! THIS IS IMPORTANT } } ``` ## Header guard **In C++, you cannot define the same thing twice!** ```cpp= int x = 1; // int x = 2; // WRONG: define x twice struct S {}; // struct S { int x; }; // WRONG: define S twice int foo(int) { return 0; } // void foo(int) {} // WRONG: define foo twice class C { int x; }; // class C {}; // WRONG: define C twice ``` **#include "xxx" is equivalent to copy "xxx"** ```cpp= // header.h // ======================= int x; struct S {}; void foo(); class C {}; // file1.cpp // ======================= #include "header.h" // #include means copy int y; // Equivalently file1.cpp // ======================= int x; struct S {}; void foo(); class C {}; int y; // file2.cpp // ======================= #include "header.h" #include "header.h" // WRONG: redefinition // Equivalently file2.cpp // ======================= int x; struct S {}; void foo(); class C {}; int x; struct S {}; void foo(); class C {}; // THIS IS WRONG DUE TO REDEFINITIONS!!! ``` We have to add *include guard* to prevent from this. ```cpp= // header.h // ======================= #ifndef HEADER_H // include guard #define HEADER_H // include guard int x; struct S {}; void foo(); class C {}; #endif // include guard // file2.cpp // ======================= #include "header.h" #include "header.h" // OK because of include guard // Equivalently file2.cpp // ======================= int x; struct S {}; void foo(); class C {}; // the second include is prevented // happy now ``` ## Separation of header and implementation Sometimes we want to reuse the same class. ```cpp= // file1.cpp // ======================= class C { // Assume it is super long }; int main() { C c1; } // file2.cpp // ======================= // Also want to use C, but don't want to rewrite C int main() { C c2; } ``` The solution is to **put the class definition into header file** in order to be used by other cpps. ```cpp= // C.h // ======================= class C { // Assume it it super long }; // file1.cpp // ======================= #include "C.h" int main() { C c1; } // file2.cpp // ======================= #include "C.h" int main() { C c2; } ``` However, it is too long for some classes to write all implementations of methods in a single file. So we **separate the definition of methods apart from the class definition**. ```cpp= // C.h // ======================= #ifndef C_H #define C_H class C { double x; // all properties have to be put here (in the header) public: C() { x = 0; } // we can define method in the header ~C(); // Or we can also define method in another file private: double foo(int); // This is method declaration static string bar(char a, double b); }; #endif // C.cpp // ======================= #include "C.h" // (1) include header // define destructor of class C C::~C() { // (2) classname::~classname() {...} // define here x = 0; } // complement definition of `double foo(int);` double C::foo(int x) { // (3) return_type classname::method_name(parameters) {...} this->x = x + 1; return this->x; } string C::bar(char a, double b) { // define here return {}; } ``` ```cpp= // original_optionalint.cpp // ============================= class OptionalInt { // properties int* value; public: bool has_value() const { // return hasvalue == true; return value != nullptr; } int value_or(int val) const { if (has_value()) { return *value; } else { return val; } } void clear() { if (has_value()) { delete value; } value = nullptr; } void set_value(int val) { *value = val; } OptionalInt(int val) { value = new int{val}; // `new` allocates a fresh space on memory // `int` indicates the space contains an int // `{val}` specifies the value contained in the space // `=` assigns address of the space to `value` } OptionalInt() { value = nullptr; } ~OptionalInt() { clear(); } }; // optionalint.h // ============================= class OptionalInt { // properties int* value; public: bool has_value() const; int value_or(int val) const; void clear(); void set_value(int val); OptionalInt(int val); OptionalInt(); ~OptionalInt(); }; // optionalint.cpp // ============================= #include "optionalint.h" bool OptionalInt::has_value() const { value != nullptr; } int OptionalInt::value_or(int val) const { if (has_value()) { return *value; } else { return val; } } void OptionalInt::clear() { if (has_value()) { delete value; } value = nullptr; } void OptionalInt::set_value(int val) { *value = val; } OptionalInt::OptionalInt(int val) { value = new int{val}; // `new` allocates a fresh space on memory // `int` indicates the space contains an int // `{val}` specifies the value contained in the space // `=` assigns address of the space to `value` } OptionalInt::OptionalInt() { value = nullptr; } OptionalInt::~OptionalInt() { clear(); } ``` ## Homework: Crazy Eights ``` Rule of Crazy Eights 1. Each player deals 7 cards 2. The rest cards are placed into a deck 3. The top card of deck is known to both of players Start 4. Each player plays a card with the same suit or the same rank with the top card of deck and puts onto top of the deck 5. `Eight` can be seen as any rank and any suit 6. If the player has no card with the same suit or rank with the top card of deck, then he draws the top into his hand 7. The game terminates if there is no card in the deck or in the hand of player 8. The player with the least amount of cards in hand wins ``` ``` Features 1. randomly shuffle 52 cards 2. deal 7 cards to both players 3. Play game - Print current state of the game - cards in the hand of human player - card on the top deck - Prompt to play card / draw card - If the top card of the deck is 8, prompt to select a suit and rank 4. Decide whether the game ends and announce the winner 5. Ask the player if want to replay ``` ```cpp= class Card { int rank, suit; public: Card() { ... } ~Card() { ... } int get_rank() const { ... } int get_suit() const { ... } }; class Deck { Card cards[52]; int n_cards; public: Deck() { } ~Deck() { } int get...() { } } ``` ## String ```cpp= #include <string> int main() { std::string str{"hello"}; // hello str.append(" world"); // hello world std::string str2; // "" std::cout << str2.empty(); // true str2 = std::to_string(5); // 5 str2.append(std::to_string(7)); // 57 for (int i = 0; i < str2.size(); ++i) { // size: 2 std::cout << str2[i] << "-"; // 5-7- } // h e l l o _ w o r l d // 0 1 2 3 4 5 6 7 8 9 10 int pos = str.find("world"); // 6 int pos2 = str.find("World"); // -1 str.erase(3, 2); // str: hel world str.insert(3, "lo"); // str: hello world std::cout << str.substr(6, 4); // worl } ```