Home

C++ notes - OOP

Written by ZHANG Rushan.

Note: "poor guy" == "temporary object"

0. Cautions

    1."Automatic 0-initialization"
        1. global
        2. STL

    2."Where to put keywords?"
        must only be in function declaration "inside class"
            1. override
            2. virtual
            3. explicit
            4. =0       // no definition
            5. =default // no definition
        must be both at declaration and definition
            1. const
            "const keyword must be closest to the function parameter list"

    3."be careful when using accessor is necessary"
        e.g. when accessing private of parents
    
    4."do not forget to use the default ones given for free from compiler"
        e.g.
            operator=
            copy constructor
    
    5."do not forget to use dynamic cast"
        dynamic_cast<type_cast_name>(variable_name)
        necessary when
            accessing data of child
            using child type pointer/reference to point at parent pointer/reference
                child type pointer/reference can not point to parent pointer/reference
                even if the parent pointer/reference is pointing to a child

    6."do not forget to add template declaration"
        template <typename T>
        class_template_name<T>::function_name()

        in "class body"
            no need to add <T> anywhere except for friend function
                for friend function, has to declare another template
                template <typename S>
        outside "class body"
            has to add <T>:
                1. return type
                2. scope
                3. everything in friend function
                    key reason: friend function is not member function
                    in declaration of friend function in "class template"
                        "has to name another template"
                4. another independent "class template"
            
            optional:
                everything within local scope, including parameter list
                
            definitely do not add <T>:
                constructor name
                "class defined in its own class body"
                    e.g. BSTnode
                    this is because, BSTnode is not a template
            
            good practice:
                add everywhere except cases that we definitely should not add <T>
    
    7.can call member function in all member functions including constructors and desctuctor

    8.do not forget to declare function template again for friend function

    9.do not use non-static data members as default value for member functions

01. class revision

   1."class = data members + member functions"
      class Person
      {
         private:
            char* _name;
            int _age;
            Person *_father, *_mother, *_child;
         // it is a convention to have an underscore for data members
         public:
            Person(......);
            // constructor
            // same name as the class
            // no return type
            // cannot be called
            // will be called when initialized

            ~Person();
            // destructor
            // will be called before an object is
    
            Person* father() const;	// const: will not change the data members
            Person* mother() const;
            Person* child() const;
            // accessor, returning the data members
    
            void have_child(Person* baby);
            // mutator, modifying the data of the data members
      }
    
      "Constructors are usually put public, if not public, it cannot be called successfully"
      "You are not allowed to put const on constructor"
         Person() const;   // not allowed
      "If the constructor does not require any parameter, do not add '()'" 
         Person obj;    // correct
         Person obj();  // incorrect
         // will be taken as function declaration

   2."object"
         Person desmond;
         // this is an object called desmond
         Person john;
         // this is an object called john
         Person tom;
         // this is an object called tom

   3."header file"
      "class_name.h"
         interface of the "class"
         excluding the member function implementation
      "class_name.cpp"
         including the member function implementation
      Why to do so?
         send .h file to others as interface,
         and also the compiled .cpp code(binary code)
            in this way, others cannot know the code used to implement the class
            "keeping your code confidential"
      e.g.
         ".h file"
         // interface
            #include <iostream>
            using namespace std;
            
            class Person
            {
            private:
               char* _name;
               int _age;
                  Person *_father, *_mother, *_child;
            public:
               Person(const char* my_name, int my_age, Person* my_father = nullptr,
                        Person* my_mother = nullptr, Person* my_child = nullptr);
               ˜Person();
               Person* father() const;
               Person* mother() const;
               Person* child() const;
               void print_age() const;
               void print_name() const;
               void print_family() const;
               void have_child(Person* baby);
            }
         ".cpp file"
         // implementation
            #include "person.h" // the .h file
            #include <cstring>
    
            // constructor
            Person::Person(const char* my_name, ......) // including all the data members
            {
               _name = new char[strlen(my_name)+1];
               strcpy(_name, my_name);
               ...... // assigning all the data members
            }

            // destructor
            Person::~Person() {delete [] name;}
    
            // accessor
            Person* Person::father() const { return _father; }
            Person* Person::mother() const { return _mother; }
            Person* Person::child() const { return _child; }
    
            // mutator
            void Person::have_child(Person* baby) { _child = baby; }
    
            // const member functions
            void Person::print_name() const
            {
               cout << (_name ? _name : "unknown");
               // ternary choice, to make sure things printed out is always valid
            }
            void print_parent(Person* parent)
            {
               if (parent)
                  parent->print_name();
               else
                  cout << "unknown";
               // before calling a member function of a class pointer, always check whether the pointer is nullptr
            }
    
         "do not forget the "Person::" for scope"
         "do not forget the "[]" when deallocating array pointer"
         "always use "const" when modifying member data is not necessary"
            "add 'const' in both declaration (in class) and definition (outside class)"
            ""const" is not allowed for constructor"
    
         "the "*" goes with the variable, not with the data type"
            int *p, q;
            // p is a pointer 
            // q is an int
         "before calling a member function of a class pointer, always check whether the pointer is nullptr"

   4."member functions includes:"
      constructor
      destructor
      accessor // returning member data
      mutator  // changing member data
      constant member function

   5."struct vs class"
      data in "struct" is public by default
      data in "class" is private by default
         struct {...}
            // same as class { public:...}
         class {...}
            // same as struct { private:...}

      "struct in C" can only carry data members
      "struct in C++" can carry both data members and member functions

   6."you can declare a class, and define it later"
      "it is okay to include multiple class declarations"
      when declaring "friend class", it is no need to declare that "class" in advance

   7."data members"
      1.can be any basic type

      2.can be user-defined types already defined
         can not be user-defined "class" that is declared but not defined
    
      3.can be "pointer" or "reference" of user-defined types already "declared"
         can not be the "class itself" that is not defined
      
      4.can be the "pointer" of the being-defined "class"
         can not be the "class itself"

         class Cell;
         class List
         {
               int size;
               Cell* data; // allowed
               Cell x;     // not allowed
         };
         class Cell
         {
               int info;
               Cell* next;
               Cell xl;		// not allowed
         };

   8.usually, we would make the data member of link list nodes public
      faster than accessor
      we can use "struct"

   9.default value for Non-static Members
      you can define and assign number to "data members" at the same time
         "only for C++ 11 or later versions"
         use either "=" or "{}" initializer
         can not use "()" initializer

   10."{}, () initializer"
      "only for C++ 11 or later versions"
         int a{1};
         int a(1);
         // () is not allowed in class data member initialization

      // similar to class constructor
      ""{}" doesn’t allow us to do narrowing conversion, but "()" and "=" do"
      ""()" is not allowed in class data member initialization"

   11."inline function revision"
      functions defined "within class body" are by default inline function
         However, it would be more readable to put "inline"
      for functions "outside class body", and "still in the same file or in the included header file", 
         you can specify "inline"
         "this specification is mandatory"
         e.g.
         inline Person* Person::child() const{return _child}
            only be fulfilled if placed "in the same file"
      the inline keyword can be placed at declaration or definition or both

   12."scope operator"
      int height = 10;
      class Weird
      {
         short height;
         Weird();
         void setHeight(int height){
            this->height = height;
            Weird::height = height; // okay, but rarely used
            // this->height: data member
            // height: parameter
         }
      };
      Weird::Weird(){
         height = 15;    // referring to the data member
         ::height = 16;  // referring to the global variable
      }
      // precedence: function parameter > data member (this->) > global variable (::)

02. basic syntex revision

   1."must initialize with the value when declaration"
      reference variable
      const variable
      const pointer
      
   2."uses of &"
      bitwise and logic
      reference variable
      address

   3."uses of *"
      multiply
      pointer variable
      dereference

   4."pointer vs reference"
      pointer can point to nothing, reference should bound to another variable
      pointer can to point at difference variables at difference times
      many symbols working with pointer, but not with reference variables
         *
         ->
   5."pointer arithmetic"
      +,-
      the value change of pointer
         int x;
         int *p = &x;
         p = p + 1;
         // adding the size of x (int, 4 bites) to p
      the position change of pointer
         int x;
         int *p = &x;
         p = p + 1;
         // moving downward one position
     "it is possible to have a pointer subtract another pointer"
         int x, y;
         int *p = &x;
         int *q = &y;
         cout << p - q << endl;
         // you get 1 here

      how is address allocated in stack?
         int x = 20, y = 10;
         short a = 99, b = 9;
      in the memory space "stack"
         // the "0x7ffeeb6b982e" are memory address
         // each one is a memory cell, 1 byte
         "int x" // = 20
         0x7ffeeb6b9839
         0x7ffeeb6b9838
         0x7ffeeb6b9837
         0x7ffeeb6b9836 // the address of x
         "int y" // = 10
         0x7ffeeb6b9835
         0x7ffeeb6b9834
         0x7ffeeb6b9833
         0x7ffeeb6b9832 // the address of y
         "short a" // = 99
         0x7ffeeb6b9831
         0x7ffeeb6b9830 // the address of a
         "short b" // = 9
         0x7ffeeb6b982f
         0x7ffeeb6b982e // the address of b

      comparisons
         <
            pointing to a lower memory than
         >
            pointing to a higher memory than
         ==
            pointing to the same memory

   6."return by reference"
      only return by reference if the object returned will not be killed
         "never return by reference a static object"
      will not create a temporary memory space when returning 
         "exception: const reference to wrong type or const literal"
         less expensive
      "to be more specific, this is lvalue reference"

      "lvalue":
         occupies some identifiable location in memory
      "rvalue":
         does not represent an object occupying some location in memory
         like "immediate" in MIPS (COMP2611)
   7."const key word"
      1."const normal"
         const float PI = 3.14;
         int const x = 10;
      2."const reference"
         const int &x = e;
         int const &x = e;
         // cannot be: // int& const x = e;
            cannot modify through the reference variable
            cannot be referred to by another non-const reference
               "lowering level of protection"
            can still be modified through the referred variable itself
               int e = 3;
               const int &x = e;
               x = 4;   // not allowed
               e = 4;   // allowed   
      3."const class object"
         only const member functions can be called
            "even if a member function does not modify data member, 
            if not protected by "const" word, 
            the const object cannot call it"

      4."const member function"
         cannot modify data members
      5."const related to pointer"
         1. const pointer
            int* const p = &a;
            "cannot bind to other objects"
         2. pointer to const object
            const int* r = &a;
            int const *s = &a;
            "cannot change the value of the object through the pointer"
         3. const pointer to const object
            const char *const pc = &c;
            char const *const pc = &c;

         "cannot assign a _pointer to const object_ to a _normal const_"
            int *pi;
            const int *pic = &i;
            pi = pic;   // Not allowed
         it is like, breaking an agreement

      6."const in function arguments"
         "must be added at both function declaration and definition"

03. advanced class

   1."this"
      "this" is the automatically generated pointer pointing at the things I use to call the function
         class Person{
            private:
               int a;
            public:
               Person(int aa){
                  this->a = aa; // the same as a = aa;
               }
         }
      "used to resolve name conflict"
         setHeight(int height){
            this->height = height;
            // this->height: data member
            // height: parameter
         }

         class Number{
            private:
            int n;
         public:
            Number(int n){
               this->n = n;
            }
            Number add1(const Number& number) const
            // this first const: protecting the parameter
            // this second const: protecting the data member
            {
               int result = n + number.n; // this is not protected by private
               Number new_number(result);
               return new_number;
            }
         }

      "we can access the private data of another object in the member function of the same class"
         int result = n + number.n;
            // in member function of class Number

   2."class object initialization"
      1."{} initializer"
         if all data members are public:
            class Word
            {
               public:
                  int frequency;
                  const char* str;
            };
         you can use:
            Word movie = {1, "Titanic"};
         after C++ 11, also:
            Word movie {1, "Titanic"};
         if the number of elements in the brace-enclosed initializer list is less than number of public data members:
            the other ones will be initialized with 0
         can not be used if:
            1. there are user-defined constructors
            2. some data members are private

      2."constructors"
         0."summary"
            1. default constructor
               nothing
            2. type conversion constructor
               = or ()

    
         1."default constructor"
            "input nothing"
               X::X() {……}
               X::X(int a = 2, float b = 3.4) {……}
            if not defined, it will generate a default constructor, which is empty
               i.e. X::X() {}
                  "this is automatically generated if and only if you don't have any constructor"
                  "once you have a constructor, 
                     even if not default constructor, 
                     this will not be automatically generated"
    
         2."type conversion constructor"
            = or () or {}
            converting "one" variable "(int, string etc.)" into object
               Word(char c) 
               {
                  frequency = 1; 
                  str = new char[2]; 
                  str[0] = c; 
                  str[1] = '\0';
               }
               Word(char c, int k = 2) 
               {
                  frequency = k; 
                  str = new char[2]; 
                  str[0] = c; 
                  str[1] = '\0';
               }
            "implicit"
               = 
                  "the "=" has nothing to do with assignment"
               "more expensive"
            "explicit"
               new Word();
               ()
               "less expensive"
    
            implicit conversion by surprise
               1.
                  Word::Word(const char*s)
                  {
                     frequency = 1; str = new char[strlen(s) + 1]; strcpy(str, s);
                  }
                  void print_word(Word x) {x.print();}
                  int main() {print_word("Titanic");}
                     "this will work"
                     void print_word(const Word& x) {x.print();} "will also work"   "test and run"
                        it will automatically call the implicit converter 
                           Word::Word(const char*s)
                        to stop this feature:
                           explicit Word::Word(const char*s)
                           "in this way, "=" construction is not allowed"
               2.
                  Word x;
                  x = "Titanic";
                     "this will work if and only if operator= is defined"
                        it will automatically call the implicit converter and create a poor guy
                           Word::Word(const char*s)
                        then, operator= will be runned
                        then, the poor guy will be destructed
   
            the use of explicit keyword:
               to stop unexpected behavior
               to make it less expensive
         3."copy constructor"
            Word array2 = array1;  // implicit
            Word array2(array1);   // explicit
            Word array2{array};    // explicit

            the use of explicit keyword:
               same as conversion constructor

            "Return value optimization (RVO)"
               will avoid creating poor guy
               to stop optimization:
                  "-fno-elode-constructors"
               e.g.
                  class Word
                  {
                     private:
                        // storing a char[]
                     public:
                        Word(const char *s, int k = 1) {/*converting: from char to Word object*/}
                        Word(const Word &w)  {/*copy*/}
                        void print() const { cout << str << " : " << frequency << endl; }
                        Word to_upper_case() const
                        {
                           Word x(*this);
                           // do something
                           return x;
                        }
                  };
                  int main()
                  {
                     Word movie("titanic");
                     Word song = movie.to_upper_case();
                  }
               without RVO:
                  copy constructor will be called at:
                     Word x(*this);                         // direct call
                     return x;                              // creating a poor guy
                     Word song = movie.to_upper_case();     // using copy constructor to create song
               with RVO:
                  copy constructor will only be called at:
                     Word x(*this);                         // direct call
            class_name(const class_name& object_name);
            "&"
               save memory
               without this: infinite recursion
                  const MyArray arr
                  when passing, becomes:
                  const MyArray arr = arr1;
                  this will call the copy constructor again
                  and this will be done recursively
                  therefore, this is forbidden
                     "compilation error"
            "const"
               to avoid changing the object passed to the function
            "default copy constructor"
               if we do not define a "copy constructor"
                  a default copy constructor will be automatically generated
                  "even if we have other constructors"
                  it will do "shallow copy" (memberwise copy)
                     "works well for array"

               class MyArray{
                  private:
                     int* p;
                     int n;
                  public:
                     MyArray(int n){
                        p = new int[n];
                        this->n = n;
                     }
                     // copy constructor, shallow
                     // if we do not define a copy constructor, 
                     //    c++ will automatically generate a constructor just like this
                     MyArray(const MyArray& arr)   // reference to arr
                     {
                        p = arr.p;  // pointing to the same array, shallow copy
                        n = arr.n
                     }
                     // copy constructor, deep
                     // have to be defined by user
                     // if a user-defined copy constructor is defined
                     //    c++ will not generate a default copy constructor any more
                     MyArray(const MyArray& arr)   // reference to arr
                     {
                        n = arr.n
                        p = new int[n];
                        for (int i = 0; i < n; i++)
                        {
                           p[i] = arr.p[i]
                        }
                     }   
               };
               int main(){
                  MyArray array1(5);
                  MyArray array2 = array(1);
               }
            "default memberwise assignment"
               Unless you redefine the assignment operator "=" for a "class"
               the compiler generates the default assignment operator function 
               "memberwise assignment" for it
                  "works well for array"

               Different from the default copy constructor, the default assignment
                  operator= will perform memberwise assignment by calling the
                  assignment operator= of each data member
                     "the = for object definition is not assignment"

         4."member initializer list MIL"
            syntex:
               : member1(expression), member2(expression), ...
            "creating and initialzing data member at the same time"
            "higher priority than default value"
            key reason why:
               "At the time you enter {} of constructor, all the data member has ALREADY been created"
            class Haha{
               private:
                  int a;
                  int b;
                  const int c = 20;
                  int& hoho = hehe;
               public:
                  Haha(int a, int bb, int &haha) : a(a), b(bb), c(10), hoho(haha) {}
            };
            the variable inside () or {}:
               refer to "parameter" or "global variable"
            Why MIL?
               1.to resolve name conflict
                  a(a)    // this->a = a
                  a{a}
                  b(bb)
                  b{bb}
               2.to initialize const data member
                  c(10)    // will overwrite the default value const int c = 20;
                  c(bb)
                  "if you do not assign default value for const data member"
                     "then there will be compilation error if one of your constructor does not initialize it with MIL"
               3.to initialize reference variable

               4.to specify the constructor for data member objects and "base class"
                  hoho(haha)  // conversion constructor
                  hoho()      // default constructor
                  "if not initialized in MIL, it will be initialized with default constructor"
                     "remember to check whether default constructor is still available"
                  "the () can be empty for class, but not for normal variables like int"
                  class Word_Pair
                  {
                     private:
                        Word w1; Word w2;
                     public:
                        Word_Pair(const char* s1, const char* s2) : w1(s1,5), w2(s2) {}
                  }
                  "if we write this:"
                  Word_Pair(const char* x, const char* y) { w1 = x; w2 = y; }
                     1. initialize w1, w2 using default constructor
                     2. w1 = x;
                        this will run the conversion constructor using x, creating a poor guy
                        and then pass poor guy to w1 using operator=
                        after that, destructor is run for poor guy
                  "sequence of contruction"
                     always follow the sequence in data member list
                        not following the sequence in MIL
                        MIL only specify how to initialize, not the sequence
               5.delegating constructor
                  Word(const Word& w) : Word(w.str, w.frequency)
                  // will make use of:
                     Word(const char* s, int f = 1)
         5.defaults
            "compiler generated:"
            1.default constructor
               // no other constructor are defined
            2.default copy constructor
               // no other copy constructor is defined
            3.default (copy) assignment operator function   // (operator =)
            4.default move constructor (std=c++11)
            5.default move assignment operator function (std=c++11)
            6.default destructor
            "=default; =delete"
            Word() =default;  // Still want the simple default constructor
            Word(const Word& w) =delete;  // Do not want the default copy constructor
            // only for outsiders
            "listen again?"
            1.=default
               Sill want the "compiler generated constructors" if we have already defined a user-defined constructor
               Can be used even if it is going to generate one
                  "improve readability"
            2.=delete
               Do not want the "compiler generated constructor"
               Can be used even if it is not going to generate one
                  "improve readability"

            "if a class does not have pointer"
               no need to define:
                  1.copy constructor
                  2.destructor
                  3.operator=
   3."more about = {} ()"
      "normal local and global variables"
         () {} =
      "class data member"
         {} =
      Word word1 = word2;
         copy constructor, not assignment
      Word w();
         This is a function prototype
         This will not call default constructor
      using {} is much safer
         Word w{};   // this will call default constructor
   4."function overloading"
      same name, different parameter type
      "exceptions"
         const and non-const functions
         const object: calling the const functions
         non-const object: calling the non-const functions
      default values
         either at declaration or definition, "not both!"
            usually put at declaration in .h file
   6.memory layout
      static:
         stack
         deleting a static object will cause runtime error
            "double delete error"
         "test and run?"
      dynamic:
         heap
      "do not forget [] when deleting dynamic array"
      double deallocation:
         { 
            Word bug("bug", 4); 
            x = bug; // memberwise copy
         }
         out of scope:
            bug killed
            pointer deallocated;
            when killing x:
               pointer deallocated again;
   7."has/own"
      "has a" relationship
         class Tail {};
         class Person {
            private:
               Tail t;
         };
         // Person has a Tail
         "this is not good, because this is meaningless"
      "owns a" relationship
         class Tail;
         class Person {
            private:
               Tail* t;
         };
         // Person owns a Tail
         // don't have when born


   8.sequence of construction and destruction
      1.before entering the constructor: '{'
         initializing all the data members
            "in the sequence of data member list"
            not MIL
      2.nothing done before entering destructor
         before leaving destructor '}'
            deallocating all the data members
            "in the reverse sequence of data member list"
         the reverse order of construction "(for static)"
      "remember to check MIL to see how to construct"
   9. class Student : public Person {};
   "public?开头"
      All member function and all data member of "class Person" will be added to "class Student"
         Except the "constructors" and "destructors"
            All the other things, including "operator=", will be grabbed
      To initialize the data member from "class Person":
         You are not allowed to initialzed with constructor of "class Student"
         It must be done with constructor of "class Person"
            Called using "MIL"
            If there are other things in MIL, data member from "class Person" will always be intialized first
               "Regardless of the sequence in MIL"
      "Uses of MIL"
         1.to resolve name conflict
         2.to initialize const data member
         3.to initialize reference variable
         4.to specify the constructor for data member objects
         5.delegating constructor
         6.to initialize the data member inheritaged
            if skipped: an MIL of default constructor will be added by C++
   10.features of OOP:
      "class"
      "object"
      "inheritance"
      "polymorphism / lizkov"
      "dynamic binding"
   11.inheritance
      if there are some code replicated:
         if we use copy and paste:
            error pron (hard to fix errors)
      good practice:
         use syntex to reuse
      e.g.
         UPerson: name, dept  // replicated members
         Student: GPA, course_enrolled, num_courses; Teacher: rank, research_area;
      Terminology
         UPerson: "Base class"(C++) or "Super class"(Java) or "Parent class"
         Teacher: "Derived class"(C++) or "Sub class"(Java) or "Child class"
      
      "Derived class" can call "base class's" member function
      "Base class" can not call "derived class's" member function
      
      "Derived class" can not directly access "base class's" private data

      "Polymophic/liskov substitution principle"
         "Parent class" pointer can point to "child class"
         "Child class" pointer can not point to "parent class"    // impolite
         "Parent class" reference can bond to "child class"
         "Child class" reference can not bond to "parent class"   // impolite
         in general:
            Parent xx = Child xx // allowed
            Child xx = Parent xx // not allowed
         However, even if a "Parent class" pointer is pointing at a "child class object"
            it can "only" access data member and member function that is available to
               "parent class objects"
         Good use: reusing functions
            "Function Expecting an Argument of Type"        "Will also accept"
            Parent                                          Child
            pointer to parent                               pointer to student
            parent reference                                child reference

            Overload
               void print_label(const Parent& parent){...}  // No.1
               void print_label(const Child& child){...}    // No.2
               // in main:
               Child tom(...); print_label(tom);      
                  // this will call No.2
                  "Perfect match"
               Parent& tom2 = tom; print_label(tom2);
                  // this will call No.1
                  "Cannot call child member functions"
                  (only care about type of tom2)
         
         sequence of construction and destruction
            Construction:
               In the sequence from grannest parent to child
               To specify the constructor for parent, use MIL
                  If no MIL specified, C++ will automatically generate an MIL calling the default constructor
                  "Such MIL will construct parent first, then construct data members"
            Destruction:
               In the reverse order of construction "unless it is a dynamic object"
         slicing
            copying "object" 
               not "object pointer" or "object reference"
            e.g.
               Parent parent = child;
                  // calling copy constructor of Parent (Liskov)
               parent = child;
            in these cases, some data members are lost
      data member name conflict
         e.g.
         class A {
            public:
               int a;
               void f() { cout << "very happy" << endl;}
               void print() { cout << a << endl; }
         };
         class B : public A {
            private:
               int a;   // this is allowed
            public:
               void f() { cout << "not so happy" << endl;}
               void print() { cout << b << endl; A::print(); }
                  // A::print() in print -> overriding
               void m() 
               {
                  a = 20;  // the a of B
                  A::a = 30;  // the a of A
               }
         };
         int main() {
            B obj;
            obj.f(); // f() in B (not so happy)
            obj.A::f(); // f() in A (very happy)
         }
      access control
         1."protected"
            "child class" cannot access "private" data member of parents, but can access "protected" data members
            e.g.
            class UPerson
            {
               protected:  // can be accessed in derived classes
                  string name;
                  Department dept;
               public:
                  UPerson(string n, Department d) : name(n), dept (d) {}
            }
            if there are protected data members:
               you are encouraged to inherit
            if the protected data is of another object
               "child class" can only access that protected data if it is the "same" "child class"
         2."friend"
            friend class A;
               // A can be defined later
            if A is declared as friend in B, then A can access all the data members in B "including private"
               including private inherited from B in any child object of B
            such declaring friend can only be made in B, and can be placed anywhere inside "class B"
               can be under private, protected or public
      "static/early binding" <-> "dynamic/late binding"
         "early"
            when compiling
         "late"
            during execution
         "whenever there is a virtual keyword, we use dynamic binding"
            "only in this way can we decide the function to call"
         "static binding"
            without virtual keyword, then it will only call the member function of its own type
            e.g.
               t = static_cast<Teacher*>(&uperson); t->person;
               // UPerson is the parent of Teacher 
               "this will call the print of Teacher"
                  "this is doable, however, this could be problematic"
                  "not recommended"
            "or, you can use ::"
         "dynamic binding"
            "binding only happens when overriding is possible"
               "not possible cases"
                  not identical prototype
                     e.g.  
                        int vs const int&
                        int vs long
                        const Parent& vs const Child&
                  const member function vs non-const member function
                  not identical return type
                     exceptions:
                        reference/pointer of the classes of parents and childs
            virtual keyword
               "only for member function"
               e.g.
                  class A {
                     public:
                        virtual void f() {cout << "f in A" << endl;}
                  };
                  class B {
                     public:
                        void f() {cout << "f in B" << endl;}
                  };
                  class C {
                     public:
                        void f() {cout << "f in C" << endl;}
                  };
                  int main() {
                     A* p1 = new B;
                     B* p2 = new C;
                     p1->f(); "this will print out 'f in B'"
                     p2->f(); "this will print out 'f in C'"
                     p2->A::f(); "this will print out 'f in A'"
                  }
               when there is virtual,
                  it will call the function of the object it is pointing to
               when there is a virtual in a member function of a "base class",
                  all the same-prototype function in the "derived classes", 
                  including those in its "grandson class"
                  are all virtual
                  "when there is no f() in B"
                     such rules also holds
                     because the f() in "A" will be inherited by "B"
               p->A::f();
                  "this is static binding"
               e.g.
                  class A
                  {
                     public:
                        virtual void f() const {cout << "A" << endl;}
                  };
                  class B : public A
                  {
                     private:
                        void f() const {cout << "B" << endl;}
                  };

                  int main(){
                     A* p1 = new B;
                     B* p2 = new B;
                     p1->f(); "this is doable (it is like a loop hole in C++)"
                     p2->f(); "this will cause error, it will check the private one first, then check the one inherited"
                  }
         "polymorphism"
            "poly" "multiple"
            "morphos" "shape"
            same name + different 
            1. function overloading
            2. function overriding
               "polymorphism in our course means overriding"
               only overriding virtual function can be called override
               overloading:
                  during compilation time
               overriding:
                  during runtime
            4. template

         "RTTI(Run-Time Type Information)"
            "checking the type of object we are pointing at"
            1. syntex
               1. remember to
                  #include <typeinfo>
               2. input: "object" or "class"
                  "remember to dereference when checking type of pointer"
                  typeid(*p); // *p: the thing pointed by p
               3. typeid(*p)
                     returning a type-info object
                  typeid(*p).name()
                     will call name() of type-info object
                     will out-put
                        "3Abc" or "Abc"   // depending on platform
               4. typeid(*p) == typeid(B)
                  // *p is an object, B is a class
                  "normally, we can not allowed to directly compare two objects"
                  however, we have operator== in type-info object
            2. getting the type of object a pointer is pointing to  
               only work when there is virtual keyword in the "class of the pointer"
                  else, this will return the type of the pointer, not the type pointer is pointing at
               e.g.
                  A *p = new B;
                  cout << typeid(*p).name(); // *p: the thing pointed by p
                  // it will returns an object that carries the type information for us
                  when A does not have virtual:
                     typeid will return type A
                     "wrong!"
                  when A has virtual:
                     typeid will return type B
                     "correct!"
         "dynamic cast"
            Dynamic cast VS Static cast:
               Dynamic cast will help you check whether such conversion is doable
               If not doable, it will return nullptr
               e.g.
                  A *r = new A;
                  B *s = dynamic_cast<B *>(r);
                  // not doable
                  "s will be nullptr"
               Doable:
                  child pointer to parent pointer
            Dynamic cast rely on type info to work
               but you do not have to 
                  #include <typeinfo>
            If there is no virtual in A,
               then dynamic_cast will give "compilation error"
               but typeid() will not give "compilation error"
            Use dynamic cast to play safe:
               after casting:
               if(ptr == nullptr)
         "override keyword"
            checking whether a function is doing overriding
            e.g.
               in "class A"
               virtual void f()
               in "class B" (child of A)
               void f(int a) override {}
               // this is not function riding, since prototype does not fit
                  "will give compilation error"
            e.g.
               in "class A"
               void f()
               in "class B" (child of A)
               void f() override {}
               // this is not function riding, since not virtual
                  "will give compilation error"
            "syntex"
               if there is also keyword const,
                  should be placed as:
                     const override
               only put in "prototype"
                  do not place at implementation "outside class"
         "when to put virtual?"
            see slide

      "only to be put in prototype in class"
         explicit
         override
         virtual
      "to be placed both"
         const
      
      "Function call within a function"
         is like:
            this->f()
         this is the this of the function
            "will be influenced by override"
         However, such behaviour will not happen in constructor and destructor
               
      "interesting thing"
         when calling, it will check the "pointer" type "class" first
            if it touches "virtual", it will seach its child functions

   12."ABC"
      "pure virtual function"
         function without implementation
      "abstract base class"
         "class" that has at least one pure virtual function

         virtual void getName() = 0;
         // I am not going to implement this one
         "remember to put virtual"

      you can use the pointer of "abstract base class"
      you can use reference of "abstract base class"
         
      but you can not use the "abstract base class" itself
      you can not use "PBV", "RBV", explicit "conversion" with "abstract base class"
         "explicit conversion ask later"
         you can not call pure virtual function of a constructor // or destructor?
      it is possible that we have a destructor "pure virtual function"
         but it does not make sense
         since virtual destructor must be called


      if a "derived class" also do not define the "pure virtual function" in an "abstract base class"
         then the "derived class" is also an "abstract base class"
      if a "derived class" do define all the "pure virtual function"s in an "abstract base class"
         then the "derived class" is no longer an "abstract base class"
            then it is called "concrete class"
      
      e.g.
         class Car   
         {  public:  virtual double getSpeed() = 0;};
         class BMW : public Car
         {};
         class Benz : public Car
         {  public:  virtual double getSpeed() = 0;};
         class Toyota : public Car
         {  public:  virtual double getSpeed() {return 100;}};
         Car, BMW and Benz are both "abstract base class"
         Toyota is not an "abstract base class", it is called a "concrete class"
   13."final"
      1. final inheritance
         "can not inheritage this derived class any more"
      class UPerson {};
      class Student final:public UPerson{};
      // This "final" here means no other class can inherit UPerson
      class PGstudent :public Student{};
      // This will cause error, since 
      2. final override
         "can not override this function any more"
         "final works independent of override, but relies on virtual"
      // in UPerson
      virtual void print() const;
      // in Student
      virtual void print() const override final;
      // no other derived class should override print()
      
      // in PG_student
      virtual void print() const override;      
      // error
         "sequence of key word"
            const must be at left most
            both okay:
               override final
               final override
         "override keyword can be omitted here"
         "test and run?"


   14."is"
      "is a" relationship
         // Teacher is a UPerson
   15."virtual destructor"
      if virtual put at parent "class"
         will go to the destructor of the grannest child
            destruct child using child's destructor
         after that, go back to parent's destructor
            destruct parent using parent's destructor
      
      we can also put override keyword for virtual destructor
   16."friend"
      0. friend ship
         =>: declare other as friend
         1. not symmetric
            A => B
            B =\=> A
         2. not transitive
            A => B => C
            A =\=> C
         3. not inherited
            A => B
            C derived from B
            A =\=> C

      1. friend function
         friend function is always non-member function, is always global
         friend function can put everywhere in the "class body", even can be put under private
         friend function can also be a member function from other "class"
      2. friend "class"
         member function of friend "class" can access all the data members and member functions,
         including the inherited ones
      3. to declare friend in a "class template", has to declare another template
         e.g. <typename S>
      4. friend function defined in "class body" are inline

04. advanced functions

   1."λ expression"
      defining function with no name
         int range[] = {2, 5, 7, 10};
         for (int v : range)  // put elements in range to v one by one
            cout << [](int a, int b) { return a*b; } (v, 5) << endl;
         access right
            "[]"
               "[]"
                  not accessing local variables
               "[=]"
                  reading but not changing all the local variables
               "[x]"
                  reading x only
                     "if use read only, we use the value of the local variables when it is initialized"
                     it will not change if the local variables are changed
               "[&]"
                  changing all the local variables
               "[&x]"
                  changing x, but not accessing all the other local variables
                  if use "&", we use the updated values
               "[=, &x]"
                  x changable, others only readable
               "[&, x]"
                  x only readable, others changable
               "[&y, x]"
                  x readable, y writable 
         input parameter
            (int a, int b)
         function body
            { xxx } 
         function input
            (v, 5)

      saving a λ function
         auto f = [a](int x) { return a*x*x; };
         f(14);   //calling
         "auto is not allowed in this course"
      [b](int x) mutable { return b *= x; }(20)
         this is like creating a temporary b
         you can change the temporary b, but not modifying the local variable b
      auto f = [&, a](int x) mutable -> int {a *= x; b += x; return c = a + b;}
         -> int
            force it to return int
            "if not specified, it will automatically deduce a return type"
         mutable
            allowing a to be changed
            "the changeable a is temperary"
   2."function pointer"
      e.g.
      void f(int a, double b)
      {
         cout << "f";
      }
      int main()
      {
         void (*fp)(int, double);
         fp = f;
         fp(4, 2);
      }
      fp is a function pointer
      the function it points to has to return void
         and must have parameter int, double
      // can be fp = &f, can be (*fp)(4,2), but not recommended
      Can point to lambda expression?
         yes!
      can be const?
         yes!
      when is function pointer pointing to a function template instantiated?
         when pointed! (depending on the function pointer type)

05. generic programming

   1.function template
      something to generate function
      // e.g.1
         template <typename T>
         T my_max(T a, T b) {
            return (a > b) ? a : b;
         }
         // The scope for name T ends here
      // e.g.2
         template <typename T>
         inline const T& larger(const T& a, const T& b)
         {
            return (a > b) ? a : b;
         }
         // The scope for name T ends here
      // e.g.3
         template <class T>
         inline const T& larger(const T& a, const T& b)
         {
            return (a > b) ? a : b;
         }
      "e.g.2 and e.g.3 are the same"
         <typename T> is the same as <class T>
         This is related to some history issues
         "typename" is more preferred
      The scope of <typename T> declaration ends at the end of the function template
         to use T again, <typename T> should be done again
      Whenever a function is called with a specific type,
         such function is really generated by the compiler
      "template instantiation"
            create a function by substituting T with the actual type
            done by checking the actual arguments in a function call
            "formal parameter/argument"
               the "T" of "<typename T>"
            "actual parameter/argument"
               the type of T actually used, e.g. int, string
            
      "explicit instantiation"
         cout << larger<double>(4, 5.5);
         in such way, we can pass in 4, 5.5
            without specifying the type,
               it will be implicit instantiation
         if no emplicit instantialtion:
            error!
            template does not allow automatic type conversion
      "template specialization"
      // e.g.4
         // general case
         template <typename T>
         const T& larger(const T& a, const T& b)
            {return (a<b)?b:a;}
         // exception case
         template <> // this can be eliminated, if so, it is like overloading
         const char* const& larger(const char* const& a, const char* const& b)
            {return (strcmp(a, b) < 0)?b:a;}

         no need to put consecutively, but the order matters
         the "char* const&" in specialized case must match the "T" in the general case

      "template with more than one types"
      // e.g.5
         template <typename T1, typename T2>
         const T1& larger(const T1& a, const T2& b) {return (a<b)? b:a;}
         "truncation possible"
            "if b is larger and is a double, it will be converted into int"
            even worse:
               returning a temporary object!
      "template with no parameters"
         template <typename T> T* create() {return new T;}
         you can not do:
            create();
            // cannot deduce the type!
         to fix:
            create<int>();
      "the one generated by template has lower precedence"
      "things related to templates should be put in the same file"

      type casting are function templates

      "like any ordinary functions, function template also allows declaration and definition"
      
   2.class template
      // e.g.1
         template <typename T>
         class List_Node
         {
            public:
               List_Node(const T& x) : data(x) {}
               List_Node* next{nullptr};           // <T> not needed here, since we are already in class List_Node
               List_Node* prev{nullptr};
               T data;
         };
         "only explicit instantiation is doable"
            you can not do:
               List_Node x;
            you can only do:
               List_Node <int> x;
      // e.g.2
         template <typename T>
         class List
         {
            // ...
            public:
               List_Node <T>* head{nullptr};
               List_Node <T>* tail{nullptr};
         }
         "<T> here is necessary"
            Therefore, linked list of e.g.1 and e.g.2 has the same type for all nodes
   3.nontype parameters
      this parameter, like the size for array, must be able to ne decided during compilation
      you can put a variable at template type declaration
         such variable is const
         such variable must be integral
         // can be const int, but this is not necessary
         // e.g.3
            template <typename T, int max_num_items>
            class List {
               public:
                  bool append(const T& item){
                     if(num_items == max_num_items)
                     {
                        cout << "full!" << endl;
                        return false;
                     }
                     // ...
                  }
            }
         to call:
            List <int, 5> x;
         // e.g.4
            template <typename T, int N>
            int f(T (&x)[N]){return N;}
         explanation:
            int         (&x)        [N];
            //int type  reference   of size N
            the size N is "automatically decided"
            "Can use this N to initialize array directly"
         to call:
            cout << f(a) << endl;
            "only works with reference"
         // e.g.5
         void f(int x[]) {cout << sizeof(x) / sizeof(int);}
            "wrong"
               sizeof(x) will only give the size of pointer
         void f(int (&x)[5]) {cout << sizeof(x) / sizeof(int);}
            "this will work"
               "however, size of x must be specified"
               "unless done by template"
      if template is defined
         no separate compilation
         you can not put declaration and definition in different files
   4.operator overloading
      0. general rule
         1. the number of arguments is fixed
         2. the left to right or right to left sequence of execution is fixed
         3. the precedence is fixed
         4. cannot overload operators for build-in types, can only overload operators for user-defined types

      1. operator+
         class Vector{
            public:
               Vector::Vector(double a = 0, double b = 0) : x(a), y(b) {}
               // operator+ as global function
               "2 arguments"
                  Vector operator+(const Vector &a, const Vector &b)
                  {
                     return Vector(a.getX() + b.getX(), a.getY() + b.getY());
                  }
               // to make global function operator+ accessible to private data members:
                  friend Vector operator+(const Vector&, const Vector&);
                  friend ostream& operator<<(ostream&, const Vector&);
                  // can finish the friend function definition in class body
                     // in this way, friend functions are inline functions
         };
         "do not define both operator+ as global function and operator+ as member function"
         1. "operator+ as global function"
            a + b == operator+(a, b);
            can be called by:
               obj1 + obj2
               obj1 + 2.4
               1.2 + obj2
               operator+(1.2, 2.4)
            cannot be called by:
               1.2 + 2.4   // this will call default operator+ of int
            "some rules about operator overload"
               can not change number of 
               can not change……
         2. "operator+ as member function"
            a + b == a.operator+(b);
            // operator+ as member function
            "1 argument"
               Vector Vector::operator+(const Vector& a)
               {
                  return Vector(x + a.getX(), y + a.getY())
               }
            can be called by:
               obj1 + obj2
               obj1 + 2.4
               obj1.operator+(2.4)
               obj1.operator+(obj2)
            can not be called by:
               1.2 + obj2
               // c++ will only do conversion on parameters
               // not on the stuff calling the function
            "operator+ as global function is better than operator+ as member function"
      2. operator<<
         cout is an object
            ostream type
            "do not add const for ostream"
               "we are going to change ostream when output"
         // has to be made friend
         ostream& operator<<(ostream& os, const Vector& a) {
            os << a.x << ", " << a.y;
            return os;
            // or:
            // return os << a.x << ", " << a.y;
         }
         "precedence: left to right"
            cout << " a = " << a << "\n";
            is equivalent to
            operator<<(operator<<(operator<<(cout, " a = "), a), "\n");
         if return type is void:
            can not do cascating output
            i.e.  cout << x << y; no longer supported
      3. operator+=
         cascating increment is supported
            i.e.
               int x = 1;
               int y = 2;
               int z = 3;
            x += y += z;
            "precedence: right to left"
            "must be member function"
               "can not be global function"
            Vector& operator+=(const Vector& a)
            {
               x += a.x;
               y += a.y;
               return *this;
            }
            const Vector& operator+=(const Vector& a)
               this will stop:
                  (v1 += v2) += v3; // this is not good
            void operator+=(const Vector& a)
               this will stop:
                  v1 += v2 += v3;   // cascating +=
      4. operator=
         must be defined if has pointer variable:
            1. destructor
            2. copy constructor
            3. operator=
         "must be member function"
            1.check self assignment
            2.deallocate previous pointer
            3.reallocate a new pointer
            4.copy data to the new pointer
            5.return *this
            MyArray& operator=(const MyArray& arr)
            {
               if(this != &arr)  // avoid a = a (*this != arr is illegal: no operator==)
                                 // if no checking: p and arr.p might be the same, then delete p cause trouble
               {
                  size = arr.size;
                  delete [] p;
                  p = new int[size];
                  for(int i = 0; i < size; i++)\
                     p[i] = arr.p[i];
               }
               return *this;
            }
         "use operator= to implement copy constructor"
            MyArray(const MyArray& ma) : p(nullptr)
            {
               *this = ma;
            }
            :p(nullptr)
               "deleting nullptr is allowed"
               if no ":p(nullptr)", it might delete some weird address
                  "error"
      5. operator[]
         "usually has to define 2: one for non-const objects, one for const objects"
         double operator[](int j) const  // not changeable
         {
            switch (j)
            {
            case 0: return x;
            case 1: return y;
            
            default:
               cerr << "invalid dimension!\n" << endl;
            }
         }
         double& operator[](int j)    // changeable
         {
            switch (j)
            {
            case 0: return x;
            case 1: return y;
            
            default:
               cerr << "invalid dimension!\n" << endl;
            }
         }
         index can be any type, including string and char
         "can only has one index"
         operator() can have more than one index;
      6. operator++
         both as member function and global function
         pre increment
            ++x;
            returns variable
         post increment
            x++;
            returns value
         
      6. sum
         1. member function
            operator= (if declared as global, may conflict with default operator=)
               // "automatically virtual"
            operator->
            operator[]
            operator()
            
         2. global function
            operator<<
            // can be defined in class with "friend"
            "friend function is automatically global"
            if you are to define operator<< as member function:
               ostream& Vector::operator<<(ostream& os)
               {
               return (os << '(' << x << " , " << y << ')');
               }
            then it will be called by:
               d << (cout << "vector + vector: a + b = ") << endl;
            "bad"
         3. either
            operator+=
            operator+
            operator++
            operator<
               "when we define <, > is not automatically defined"

06. standard template library (STL)

   1."Containers"
      used to store data
      implemented as "class templates"
      "storing only one type of data"
         homogeneous
      types:
         1.Sequence containers
            "linear"
            vector 
            list 
            deque //double ended queue
               map + a sequence of arrays
                  // map
                  begin() -> data[0]
                  Unused
                  Chunk0 -> Unused, Unused, data[0], data[1], data[2]
                  Chunk1 -> data[3], data[4], data[5], data[6], data[7]
                  Chunk2 -> data[8], Unused, Unused, Unused, Unused
                  Unused
                  end() -> first Unused in chunk2
               "for more storage"
                  extend the map, have more arrays

            member functions for "sequence containers"      
               front(): First element
                  // for all
               back():  Last element
                  // for all
               operator[]: Subscript operator, index not checked
                  // for vector and deque
                  "not for list"
                  "can do modification (lvalue)"
               push_back(/* data */): Append element
                  // for all
               pop_back(): Remove last element, return void
                  // for all
               push_front(/* data */): Insert element at front
                  // for all
               pop_front(): Remove first element
                  // for all
               insert(p, x): Insert x at position p
                  // fast for list, available for vector and deque
               erase(x): Remove an element at p
                  // fast for list, available for vector and deque
                  // iterator: has to use return type to jump to next
               clear(): Erase all
                  // fast for list, available for vector and deque
               size(): Number of elements
                  capacity vs size:
                     capacity: total number of memory available
                     size: number of data that is actually stored
                  
               empty(): Return true if is empty
               resize(int new_size): change the "size"
                  new_size < current_size:
                     erase data
                  new_size > current_size:
                     more 0s
               operator==, !=, <, =
                  "constructor"
                     vector<int> v(10);
                     "must do explicit instantiation"
                     "the input value will be the size, not capacity"
                     "initialized as all 0"
               "Can not assign value to an index outside range"
                  e.g.
                     v[0] = 1;   "invalid"
         2.Associative containers
            "branches"
            map
            multimap
            multiset
            set // a set of elements, no duplicate
         3.Container adapters
            "use other containers to store data, and maintain some properties"
            stack // last in first out
            queue // first in first out
            priority queue
         4.Near-containers
            "not like container, but is container"
            string
            valarray
            bitset
         
   2."Iterators"
      "pointers to point at STL members"
      begin(): get the address of the first box
      end(): get the address of the box "after" the last box
         not the last box
      e.g.
         vector<int>::iterator it = v.begin();
         *it;  // dereferencing, getting first element of v
         *(it + 1)   // getting second element of v
      good things about iterators
         "v[i] does not work for list"
         "*(it + i) works for all containers"
      const_iterator
         like const pointer
   3."Algorithms"
      highly optimized
      implemented as "function templates"
      1. find
         template <typename Iterator, typename T>
         Iterator find(Iterator begin, Iterator end, const T& value)
         {
            while (begin != end && *begin != value)
               ++begin;
            return begin;
         }
         why typename Iterator?
            to improve readability
            just a typename
         "This works for all containers in STL"
            even works for regular array
      2. find_if
         template <typename Iterator, typename Predicate>
         Iterator find_if(Iterator first, Iterator last, Predicate predicate)
         {
            while (first != last && !predicate(*first))
               ++first;
            return first;
         }
         Predicate is a function pointer that points at a function that returns bool
         This will find a value that satisfies predicate(value) == true;
      3. count_if
         template <typename Iterator, typename Predicate>
         int count_if(Iterator first, Iterator last, Predicate predicate)
         {
            int count = 0;
            for(Iterator it = first; it != last; it++)
            {
               if(predicate(*it)) count++;
            }
            return count;
         }
      4. for_each
         template <typename Iterator, typename Function>
         Function for_each(Iterator first, Iterator last, Function f)
         {
            for (Iterator it = first; it != last; it++)
               f(*it);
            return f;
         }
      5. transform
      "make sure that the size of the container getting the result is big enough"
      "return the iterator pointing to the object after the last object of result container"
      template <typename Iterator1, typename Iterator2, typename Function>
      Iterator2 transform(Iterator1 first, Iterator1 last, Iterator2 result, Function f)
      {
         for(;first != last; first++,result++)
         {
            *result = f(*first);
         }
         return result;
      }

      2.function objects
         e.g.
         class A {
            public:
               void operator() (int a) {
                  // do something
               }
         };
         An object, in "class type", that overloads operator()
         e.g.
         class A {
            private:
               int value;
            public:
               A(int value) : value(value) {}

               bool operator()(int v){return v > value;}
         };
         int main() {
            A x(10); // x is called function object
            cout << boolalpha << x(20) << endl;
         }
         "x is like a function"
         usage:
            can be the predicate for find_if function
            e.g.
            // there is an object Greater_Than
            vector<int>::const_iterator p = 
               find_if(x.begin(), x.end(), Greater_Than(limit));
      1.Array
         template <typename T>
         class Array
         {
            template <typename S>   // do not use T, redefine another template
               friend ostream& operator<<(ostream& os, const Array<S>& printed);
            private:
               T* _value;  "copy constructor, operator= necessary"
               int _size;
            public:
               "in the following, <T> is omittable"
                  "this is because we are still inside the definition of the class"
                  "however, to be consistant, it is better to include <T>"
               Array<T>(int n = 10); // Default and conversion constructor
               Array<T>(const Array& a); // copy constructor
               ~Array<T>();

               Array& operator=(const Array& a);
               T& operator[](int i) {return _value[i];}
               // ...
         }
         "member function implementation"
            "remember: template do not support separate compilation"

07. tree

   "Edge"
      棍子
   "Root"
      the node without parents
   "Parent and child"
      every node except the root has exactly 1 parent
      a node can have 0 or more children
   "Leaves"
      nodes with no child
   "Siblings"
      nodes with the same parent
   "Path"
      a sequence of nodes {n(1),n(2), ..., n(k)} such that n(i) is parent of n(i+1)
   "Length"
      number of edges on the path
   "Depth of nodes"
      number of edges to root
   "Height of nodes"
      number of edges to deepest leaf
   "Height of trees"
      height of root
   "Descendant"
      All children/grandchildren + itself
   "Proper descendant"
      Descendant excluding itself
   "Ancestor"
      All parents/grandparents + itself
   "Proper ancestor"
      Ancestor excluding itself
   
   File directory: a tree!

   Binary tree
      Strict binary tree:
         each node, 2 or 0 children
      Generic binary tree:
         each node, <= 2 nodes
      Good tree:
         balanced
   "scan through every nodes in a tree"
      L: left
      C: current
      R: right
      1."In order" L C R
            e.g.
               A
            B     C
           D E
         "D B E A C"
      2."Pre order" C L R
            e.g.
               A
            B     C
           D E
         "A B D E C"
      3."Post order" L R C
            e.g.
               A
            B     C
           D E
         "D E B C A"
      computer likes "Pre order" better
      human likes "In order" better

      expression tree
         e.g. use tree to express + - * / sequences

      binary search tree
         all nodes in left < C
         all nodes in right > C
         "fast searching"
         "can have different shapes"
            average depth log(N)
            maximum depth N
      to delete a node in binary search tree
         1. no child
            just delete
         2. one child
            pass it to parents
         3. two children
            pass the minimum in the right subtree and pass it to the killed node
            or the maximum in the left subtree and pass it to the killed node
            "the minimum in right and maximum in left can have at least one child"
         "when killing, remember to connect the descendents"
         "and disconnect to the being killed node before deleting"
      "Always make sure that the deepest one is nullptr"

08. hashing

   1.terminology
   "hash table"
      an array of some fixed size
      "key"
         element of hash table
   "hash function"
      the mapping from key to value
      "value"
         the value a key is mapped to
   "hashing"
      the whole process
   2.performance
   good for:
      insertion
      searching
   bad for:
      find min
      find max
   3.hashing
      "keys U"
         set of all possible values
      an element of hash table 'T' is a key 'k', which is a subset of 'U'
         m:= |T|, m << |U|
      hash function h(k)
         from key, get index of k
         data stored in T[h(k)]
      collision
         Two keys hashed to the same slot
         "solution"
            1. design better hash function
            2. open up a new slot
      Hash function design
         h(k) = k mod m
         e.g. m = 100, k = 1234
            k mod m = 100
               only the last 2 digits are used
               => do not make use of all digits
            => "power of 10 is bad"
         e.g. m = 4 = 2^2, k = 17
            17 = (10001)2
               only the last 2 digits of binary code are used
               => very easy to get collision
            => "power of 2 is bad"
         good m:
            prime number
            not so close to some power of 2
               almost middle of 2 "power of 2"s
         "for non-integers?"
            1. use ASCII and add up
               e.g. h e l  l  o
                    8 5 12 12 15 -> 52
               problem: sometimes same set of characters
            2. h(key) = (key[0] + 27*k[1] + 27^2*k[2]) mod m
               even characters are the same, if ordering are not the same, the value will not be the same
               problem: sometimes first 3 charaters are the same
            3. h(key) = (37^(L-1)*key[0] + 37^(L-2)*key[1] + ... + key[L-1]) mod m
               avoiding a lot of collisions
               problem: complicated, time consuming
         "collision handling"
            separate chaining
               implement as a table of linked lists
               keys: 0  -> 0  -> 10 -> nullptr
                     1  -> 81 -> 1  -> nullptr
                     2  -> nullptr
                     3  -> nullptr
                     4  -> nullptr
                     ...
               where to add new data:
                  add at root: more efficient adding
               run time no longer "O(1)"
               advantage:
                  easy deletion
               disadvantage:
                  memory allocations and deallocations in linked list: expensive
                  "new and delete: expensive"
            open addressing
               linear probing
               quadratic probing

               double hashing          // best
                  expensive computing
      1."linear probing"
         when collision happens: keep moving downwards until reaches one empty box
            insert:  89 18 49 58 69
                  0        49 49 49
                  1           58 58
                  2              69
                  3
                  4
                  5
                  6
                  7
                  8     18 18 18 18
                  9  89 89 89 89 89
            searching:
               h(k) = k mod 10,
               keep moving downwards 
                  until reaching the searched one
                  or reaching an empty area
                  "you can not remove an element from hash table"
                     "if you do so, there is a problem in searching"
                     solution: lazy deleteion
                     3 types of label:
                        "EMPTY", "ACTIVE", "DELETED"
                        instead of really removing, you mark a label "DELETED"
                        tell searching function to keep go searching when it reaches this box
                        and this box can be occupied by new elements
                  0 49
                  1 58
                  2 69
                  3
                  4
                  5
                  6
                  7
                  8 18
                  9 89
         problem
            "primary clustering""refer to ppt!!!!!!?!?!?!???????"
      2."quadratic probing"
         trial and error:
            1st time, move down by 1^2=1
            if collision again, move back
            2nd time, move down by 2^2=4
            if collision again, move back
            3rd time, move down by 3^2=9
            if collision again, move back
            4rd time, move down by 4^2=16
            ......
            insert:  89 18 49 58 69
                  0        49 49 49
                  1  
                  2           58 58
                  3              69
                  4
                  5
                  6
                  7
                  8     18 18 18 18
                  9  89 89 89 89 89
         problem:
            "secondary clustering""refer to ppt!!!!!!?!?!?!???????"
         "if table size is prime: a new key can always be inserted if the table is at least half empty"
      3."double hasing"
         h1(k) = k mod 10
         h2(k) = 7 - (k mod 7)
         when collision happens:
            keep moving downwards for h2 value until reaches one empty box
         trial and error:
            insert:  89 18 49 58 69
                  0              69
                  1
                  2
                  3           58 58
                  4
                  5
                  6        49 49 49
                  7
                  8     18 18 18 18
                  9  89 89 89 89 89
         1.size of hash table is prime
         2.hash table is less than half filled
         "analysis"
            h1(k) = k mod m
            h2(k) = R - (k mod R)
            m = 11,R = 7
            possible value by h2(k)?
               k mod 7  : 0-6
               h2(k)    : 1-7
            "m should be prime"
            "good h2(k):"
               all value generated by h2(k) is relatively prime to m
                  relatively prime: gcd(x,y)=1
               to make this possible, just pick an R that is prime number
                  that is smaller than m
      4."Re-hashing"
         load factor α = N / m
            N: number actually hashed
            when α becomes large (around 0.5), 
               double the table size // not exactly double, has to be prime
               and re-hash all data items with a new hash function
            "can really erase data in re-hasing"

09. additional knowledge

   1."for loop"
      int range[] = {2, 5, 7, 10};
      for (int v : range) 
      {
         /* code */
      }
      // put elements in range to v one by one
   2."to print out address of str/char"
      cout << reinterpret_cast<const void*>(str) << endl;
   3. ["type"] * name = new ["type"];
      ["type"] == ["type"] => must be right
         "if const, must initialize"
   4. in array, construct one by one
   5. string comparison
      "desmond" vs "peter":
         'p' > 'd'
         so "peter" > "desmond"
      "des" vs "d"
         3 char > 1 char
         so "des" > "d"
      if we compare const char*,
         it will compare addresses,
         "not the content"
   6. return by value
      when reaching the end of "}" of callee function,
         temporary objects of callee function will be killed
         therefore, another temporary object has to be generated to be returned
      RVO
         will not kill temporary object to be returned immediately,
         that temporary will live until the end of 
   7. const reference as parameters
      const reference will initialize poor guy if:
         1. const literal is assigned
         2. type mismatch
   8. we can delete nullptr
   9. const overloading
      const member function can only be called by const object, 
      non-const member function will be called by non-const object "perfect match"
      if only const member function is defined, then both const object and non-const object can call it
   10. which template to instantiate?
      template <int num, typename T>
      void foo(T a)
      template <int num, typename T>
      void foo(int a)   // this will be considered as partial specialization
      1. when specialized can be called, specialized one has higher priority
      2. every single parameter has to be used in the parameter list in other to be automatically deduced
      3. the order of the parameter list matters, and the one inputted in explicit instantiation has to be put at the front
      "standard cases"
         1. under the circumstance that no type conversion is needed, the one with less type deduction will be called
         2. if a template has to be called with type conversion, it has lower precedence
         3. any template with <> below other template is a specialization of another template. 
               it is called if and only if the deduced type fits its specialization
   11. can have a class defined in another class under private
      "in this way, only usable in the class"
   12. when defining a class, we can use the pointer to that class but not the class itself
      this rule is broken when the following is satisfied:
         the "class" is a template
         must not involve has a relationship of itself (e.g. pointer)
   13. c string char[]
      if c string operators are used
         e.g. strlen, strcpy
      has to "#include <cstring>"
   14. trap
      class A
      {
         public:
            A()
            {
               /* code */
            }

            A(const A& other) 
            {
               /* code */
            }
      };
      A a(A());   // This will cause trouble: this will be taken as a function declaraction
                  // The function parameter is A (*f) (), which is a function pointer
                  // This function pointer takes no parameter, and returns A
      A a{A()};   // This is nornal, just using a temporary object A() to call copy constructor

10. static

   exist before we run the function
      create a "class object"
   
   "class related static"
      "class static variable":
         declared inside "class body":
            static <type name> <variable name>;
         defined outside "class body":
            <type name> <class_name>:: <variable name> = <initial value>
         "cannot be used unless defined"
         "shared accross class objects"
            similar to global variables
         "can be used even if we do not have a class object"
            <class_name> :: <variable name>
         "can be accessed cross files"
      "class static function"
         "is actually like global funtion with a class scope"
         "can only make use of static data members of the class"
         can not use this->, can not call non-static functions, can not access to non-static data members
            can not access non-static data members
         can not be const, nor virtual, can not be overloaded with non-static member function of the same prototype
         "called two way:"
            <class name> :: <function name>
            <object name>.<function name>
         "can be used even if we do not have a class object:"
            <class name> :: <function name>
         "can be inherited"
            "can be overloaded by child class static functions"
      fundamentally speaking
         in "non-static class member function":
            non-static member functions are called by
               this->f();
            non-static data members are used by
               this->data;
         in "static class member function":
            static member functions are called by
               class_name::f();
            static data members are used by
               class_name::data;
            
      
   "file scope static"
      static int a;  // only "global" in file scope
      int b;         // real "global", 
                     // can be shared with other files with "extern"
      extern int a;  // using "global" from other files
                     // only for normal global
      "cannot do extern on static"
   "function scope static"
      e.g.
      int fibonacci(int n, int& calls)
      {
         static int num_calls = 0;  // Initialized only once
                                    // Will not initialize again even if called multiple times
         // ...
         fibonacci(/* something */)
      }

11. makefile

   1."Prerequisites"
      checks the last modification time and automatically regenerate the files modified

   2."Automatic variables"
      $@
         target files
      $<
         the first prerequisite
      $^
         all the prerequisites

   3."Pattern rule"
      %
         matching any non-empty substring

         in prerequisite
            refers to the same substring matched by the '%' of targets

   4."Phony targets"
      Do not refer to actual files, but actions

   5."Variables"
      "When defining"
         XXX = XXX
      "When using"
         $(XXX)

03. include guards

   #include
      works like copy and paste
      include guard can prevent including too files for multiple times

   in .h files
      #ifndef LAMP_H // for lamp.h, just a convention
      // _IOSTREAM_ for iostream
      // LAMP_H for lamp.h
      #define LAMP_H
      // object declarations, class definitions, functions
      
      #endif // LAMP_H
   "it is a good practice to write an include guard for all the .h files"