# 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
}
```