# 計算機程式 [台大公開課](http://ocw.aca.ntu.edu.tw/ntu-ocw/ocw/cou/101S112/1/V/1) ## 硬體小筆記 Chapter 1 - ### Main Memory - #### RAM(random access memory): - 存取的時間與儲存位置的排序無關。 - 存取速度很快,相較於Secondary Storage Unit/ROM(read only memory)。 - 具揮發性: 斷電後儲存的資料會消失。 - #### DRAM(dynamic random access memory) - 半導體記憶體,利用電容內儲存電荷的多寡儲存0與1。 - 現實中電晶體會漏電,會影響儲存內容的正確性,故須透過刷新來進行週期性充電(稱之為動態的原因)。 - #### SRAM(static random access memory) - 只要維持通電的情況,不需要刷新也不會丟失儲存的內容。 - 常用於CPU當中的cache memory。 - 存取速度快,但是貴。 ## Background Knowledge ### compile/ link/ executable file --- ![](https://i.imgur.com/qB7x3oF.png) --- - #### Windows System - 順序: - .cpp compile to .o (machine code) - .o linking to .exe (executable code) - 方法: - Single File - 一行指令完成 `g++ -o hello.exe hello.cpp` - 分行完成 ``` // Compile-only with -c option g++ -c Hello.cpp // Link object file(s) into an executable g++ -g -o Hello.exe Hello.o ``` - note: - -Wall: prints "all" Warning messages. - -g: generates additional symbolic debuggging information for use with gdb debugger. - Multiple File - 一行指令 `g++ -o myprog.exe file1.cpp file2.cpp ` - 分行完成 ``` g++ -c file1.cpp g++ -c file2.cpp g++ -o myprog.exe file1.o file2.o ``` - MakeFile - 建立無附檔名的makefile ``` //makefile all: hello.exe hello.exe: hello.o g++ -o hello.exe hello.o hello.o: hello.cpp g++ -c hello.cpp clean: rm hello.o hello.exe ``` - 使用指令: `make ` - 實際執行: ``` g++ -c hello.cpp g++ -o hello.exe hello.o ``` - **注意事項**: - 只會執行makefile第一行,然後根據需求往下找;故第一行需是all的資訊 - #### Unix System - 順序: - .cpp compile to .o (machine code) - .o linking to 執行檔(無檔名) (executable code) - 方法: - Single File - 一行指令完成 `g++ -o hello hello.cpp` - 分行完成 ``` g++ -c hello.cpp g++ -o hello hello.o ``` - Multiple File - 一行指令完成 `g++ -o myprog.exe file1.cpp file2.cpp ` - 分行完成 ``` g++ -c file1.cpp g++ -c file2.cpp g++ -o myprog file1.o file2.o ``` - MakeFile - 建立無附檔名的makefile ``` //makefile all: hello hello: hello.o g++ -o hello hello.o hello.o: hello.cpp g++ -c hello.cpp clean: rm hello.o hello ``` - 使用指令: `make ` - 實際執行: ``` gcc -c hello.cpp gcc -o hello hello.o ``` - **注意事項**: - 只會執行makefile第一行,然後根據需求往下找;故第一行需是all的資訊 ### Object Oriented Programming (OOP) #### 特性 - 封裝 - 把物件的屬性跟方法包在一起,在內部的屬性跟方法定義完後,外部只能透過物件提供的介面去進行操作。可以避免外部的不當操作,在使用上也可以變得比較簡單,不需要去在意內部的實作。 - 繼承 - 提升程式的重用性、延展性,可以把一些共通性很高的類別定義在同個父類別,子類別可以繼承父類別共通的屬性,再根據子類別的一些特異性去定義自己的屬性與方法。 - 多型 - 用同一個介面來實現不同的方法。 - 包含overload(靜態)跟override(動態),同名的function根據接收者不同可以對同一事件做出不同回應。 - overload是指同樣function名稱透過輸入數量或類型不同,function會做不同的事情。 - override指的是子類別,在有宣告virtual的情況下,父類別同名的function可以直接改寫父類別的function,但是父類別跟子類別function的參數必須相同。 - overwrite指的是將父類別的function隱藏起來,(覆寫掉父類別的function),父類別跟子類別同名function的參數可以相同也可以不同。 - virtual function也是多型的一個實踐,也就是看接收者是誰再來決定實作內容。如果父類別跟子類別都有同一個function,在父類別的function加上virtual,在外部做運用的時候,只要透過父類別的pointer來指父類別跟子類別的物件,就能根據他們實際上的類別來實踐這個同名的function。 #### 優點 - 可重用性、延展性、彈性 ## Array, Pointer and String Chapter 6~8: - ### Array - 分配一段連續的儲存位置 - 宣告陣列的方法: - 需要提供allocate的位置的大小 - 需要**類型**及**大小**等資訊 ``` int arr[7]; int arr2[3]={1,2,3}; ``` - 須注意的地方: - 使用到一開始宣告的陣列以外的位置 - 如該位置存在且為變數,即可以變動它 ``` int arr[3]={1,2,3}; arr[8] = arr[1] + arr[2]; ``` - 將不想被變動的值設為常數 ``` const int a = 5; ``` - 1D Array傳到function中 - pass by value ``` int a[5]; void printa(int a[]){...} printa(a); ``` - pass by reference ``` int a[5]; void printa(int (&a)[5]){...} printa(a); ``` - pass by address ``` int a[5]; void printa(int* a{...} printa(a); ``` - ### Pointer - 為一個儲存位置的地址,需要提供此位址指向的**類型** - 須透過此地址更改或存取該位址當中的值 - 宣告指向的位置及存取值: ``` int a = 5; int *p = &a; cout << 'the value of variable a is' << a << endl; cout << 'the pointer p is pointed to the address of a.'<< endl; cout << 'the place of the pointer p pointed to stores the value of ' << *p << endl; ``` > Output: > the value of variable a is 5 > the pointer p is pointed to the address of a. > the place of the pointer p pointed to stores the the value of 5. - 更改指向位置儲存的值: ``` int a = 5; int *p = &a; *p = 10; cout << 'the value of variable a now is ' << a <<endl; ``` > Output: > the value of variable a now is 10 - 須注意的事項: - 清除pointer目前指向的位置,指向0或NULL皆可。 ``` int *p = 5; p = 0; p = NULL; ``` - 宣告時可以與同類型的變數一併出現 ``` int a, *x = &a; ``` - **非常重要: 勿與Reference(參考)搞混** - Reference是一個**已經存在的變數**的**ailias(別名)** - 易與位址搞混,當作位址時該位址的變數須先被宣告 ``` int a, int &b = a; cout << 'an integer variable a was defined, while b was not defined as a variable before: b was an ailias of variable a.' << endl; int c, int d = &c; cout << 'an integer variable c was defined: d was an integer storing the address of variable c now.' <<endl; ``` > Output: > 整數變數a已定義,但b並未被定義成任何變數: b是變數a的別名/參考. (翻譯) > 整數變數c已定義: d是一個整數變數,目前儲存變數c的位址。(翻譯) - Reference的目的在於盡可能**節省空間**,減少在不必要的情況下新增一個新的變數。 ``` int addNum(int a,int b){ return a+b; } int betterAddNum(int &a, int &b){ return a+b; } void main(){ int num1 = 5; int num2 = 3; cout << '1. with allocation of new variables a and b for function addNum <<endl; cout << ' addNum(num1,num2) = ' << addNum(num1,num2) << endl; cout << '2. instead of allocation of new variables a and b, just pass the value of variable num1 and num2 by creating their ailiases and calling by references.' >>endl; cout << ' betterAddNum(num1,num2) = ' <<betterAddNum(num1,num2) << endl; } ``` >Output: > 1. 為了函式addNum,有分配位置給新的變數a及b(翻譯) > addNum(num1,num2) = 8 > 2. 取代分配位置給新的變數a及b,建立num1及num2的別名,並透過呼叫參考的方式將它們的值直接傳入(翻譯) > betterAddNum(num1,num2) = 8 - Reference 傳送的是該物件的別名,代表傳進去的其實就是那個物件,如更改那個物件的值,是**會改變該物件**的。 ``` //main.cpp //用比較混亂的寫法但比較好呈現,實際操作請把dog.cpp,dog.h分開 class Dog(){ public: Dog& olderDog(Dog& dog){ dog.age += 1; return dog; } Dog(int a){ age = a; } int getAge(){ return age; } private: int age; } void main(){ Dog apple(10); cout<<"apple's age = " << apple.getAge()<<endl; Dog apple2 = apple.OlderDog(); cout<<"apple's age now = "<<apple.getAge()<<endl; cout<<"the one return = "<<apple2.getAge()<<endl; return; } ``` >Output: > apple's age = 10. > apples age now = 11. > the one return = 11. - ### String - 字符陣列也可用來表示字串,字串宣告必伴隨截斷符號(\0) ``` char c[] = "blue"; char d[] = {'b','l','u','e','\0'}; ``` - 陣列名稱與pointer具相同功能 ``` char color[5] = "blue"; char const *p = color; char const *t = &color[0]; cout << "char color[3] is " << color[3] << " and *(p+3) is " << *(p+3) <<endl; ``` > Output: > char color[3] is e and *(p+3) is e - ### Vector - Pass 2D vector into function - 方法一: pass by value `void passVector(vector<vector<int>> v){...}` - 方法二: pass by reference - 避免pass by value造成的copy and pass `void passVector(vector<vector<int>>& v){...}` ## Classes and Data Abstraction Chapter 9~10 - ### Classes - 存取與修改權限: public, private, protected - 宣告方法 ``` class Dog{ public: void setAge(int a){ age = a; } void setGender(int g){ gender = g; } int getAge(){ return age; } int getGender(){ return gender; } int fitness(){ return weight*age/1.495*(gender+0.45); } Dog(int a,int g,int w){ age = a; gender = g; weight = w;} private: int age = 0; int gender = 0; //girl,boy int weight = 0; } ``` - 拆分成header(.h)與implementation(.cpp)檔案: - header ``` //dog.h #ifndef DOG_H #define DOG_H class Dog{ public: void setAge(int a); void setGender(int g); int getAge(); int getGender(); float fitness(); Dog(int a, int g, int w); private: int age = 0; int gender = 0; int weight = 0; } #endif ``` - cpp ``` //dog.cpp #include "dog.h" Dog::Dog(int a, int g, int w){ age = a; gender = g; weight = w; } void Dog::setAge(int a){ age = a; } void Dog::setGender(int g){ gender = g; } int Dog::getAge(){ return age; } int Dog::getGender(){ return gender; } float Dog::fitness(){ return age*weight/1.495*(gender+0.45); } ``` - 在main.cpp如何使用class: ``` //main.cpp #include "dog.h" ``` - extern 與靜態(static)用法 - extern: - 令其他cpp file可以存取其他檔案中的特定變數 ``` //main.cpp #include "dog.h" using namespace std; void main(){ cout << "a = " << a << endl; a = 0; cout << "a = " << a << endl; } ``` ``` //dog.h #ifndef DOG_H #define DOG_H extern int a; void greeting(); #endif ``` ``` //dog.cpp #include "dog.h" int a = 1; void greeting(){ cout<< "hello its me." } ``` - static(靜態) - 存在時間與主程式(main)執行時間相同,該函式結束時仍保存著 ``` int couting(){ static int counter = 0; //only initialized at the first time return counter++; } ``` - **作用域不變**,宣告於function內,可存取的位置仍是function內:防止不同檔案中相同變數名稱在link時出現錯誤 ``` //a.h #ifndef A_H #define A_H static int dds; #endif ``` ``` //b.h #ifndef B_H #define B_H static int dds; #endif ``` ``` //main.cpp #include "a.h" #include "b.h" ...... ``` ``` //sh g++ -c main.cpp //產生main.o g++ -c a.cpp //產生a.o g++ -c b.cpp //產生b.o g++ -o main main.o a.o b.o //如果未加入"static": 錯誤產生,因為main.cpp裡面include a.h以及b.h,但當中有重複變數名稱dds ``` - **非常重要!! static 使用在class當中不屬於任何物件** - 使用在class variable前: - 此variable不屬於此class的任何object,即不屬於任何instance;為**整個class共用之變數** - **初始化**的方法,**不可以在class宣告時做**,需要在class外面、main外面。 - 使用在class function前: - 此function不屬於此class的任何object,即不屬於任何instance;為整個class**共用之函式** ``` #include <iostream> using namespace std; class Object { public: Object() { ++counter; cout << "counter = " << counter << endl; } static int getCounter() { return counter; } private: static int counter; }; int Object::counter = 0; // initializing the static int int main() { Object obj1; Object obj2; Object obj3; cout << Object::getCounter() << endl; return 0; } ``` - constant(const) object: - 只能呼叫const function,無法呼叫non-const function; **但是const function可以被const object以及non-const object呼叫!** - **只能用member initializar**的方式創建物件並賦予初始值: ``` //constMember.cpp include header definition version Class Dog{ public: int get_weight() const; Dog(int a,int g); //constructor using member initializer private: int age; int gender; } int Dog::get_weight() const { return a+g/g*2;} Dog::Dog(int a,int g) : age(a),gender(g){} //constructor using member initializer ``` ``` //main.cpp #include "constMember.cpp" using namespace std; void main(){ //member initializer= //CLASS_NAME INSTANCE_NAME(first_value,second_value ); const Dog doggy(5,0); //const object Dog kitty(3,1); //non-const object doggy.get_weight(); kitty.get_weight(); //使用pointer創建物件的呼叫方法 Dog* dog_ptr = new Dog(10,1); (*dog_ptr).get_weight(); dog_ptr->get_weight(); } ``` - **一般 const 用法**: - 基本使用: `const int a = 10; //不可修改a的值` - 指標使用: ``` const int* ptr; //不可修改ptr指向的內容 int* const ptr; //不可修改ptr指向的位置 const int* const ptr; //指向的內容、指向的位置都不可修改 ``` - 回傳使用: ``` const int foo(); //函式外部不可修改回傳的值 ``` - **constant(const) member function** vs return const object: - const member function **無法改動成員屬性** ``` //passenger.h class Passenger{ public: int get_age(); const Passenger get_nextSeats(); //return回去的值是const int get_bus_number() const; //member function是const,即此函式不能改動成員屬性 private: ... } ``` - **須注意!!!的std結構operator:** - std::map中的$[]$與**at**差異: - []: 確認輸入key是否存在而返回,不存在會**insert**這個key進去;在const member function**不可以使用** - **at**: 確認輸入key是否存在而返回,不存在會throw exception;在const member function中**可以使用** - **兩個classes的object互call的極端狀況** - 解法一 - 使用header file, cpp file來儲存各自的程式碼 - **不要互相於header file include對方**,會造成程式從A->B,又從B->A,但A已經被執行過,導致B無法參考A。 - **依照實作時參考的順序,來include在header file**;而cpp當中一般是include自己的header檔,但自己的header檔無包含對方的header檔,則**需在cpp內include以知道如何實作**。 ``` main.cpp #include "A.h" #include "B.h" ... ``` ``` A.h #include "B.h" ... ``` ``` B.h ... ``` ``` A.cpp #include "A.h" ... ``` ``` B.cpp #include "A.h" #include "B.h" ``` - 解法二 - 共同放在一個source cpp,使用**前置宣告+動態配置記憶體**(pointer to class object / vector of class object),不然compiler不知道要**預留多少空間**,且可能會報錯無"對應的建構子"。 ``` main.cpp class Dog; class Cat { private: int id; string name; Dog* friend; // 全部Dog* pointer可抽換成vector<Dog> public: Cat(int i, string n, Dog* f); Dog* get_friend(); } class Dog { private: int id; string name; Cat* Cat; // 全部Cat* pointer可抽換成vector<Cat> public: Dog(int i,string n, Cat* f); Cat* get_friend(); } ``` ## Operator Overloading Chapter 11~12 - ### Function Overloading - function 名稱相同,但是作得內容不同 - 考量: - 輸入引數(parameter)數量 - 輸入引數(parameter)種類 - 輸入引數(parameter)順序 - ### Operator Overloading - 不能更改運算元(operant)的數量 - 只能使用已存在的operator - 一些無法overloading的運算子: condition operator(?:),address(&),...etc - 一些系統會自己預設的運算子: assignment(=),reference(&),comma(,),不須特別做。 - assignment(=): - 如未overload, class object之間會直接用copy的方式->創建一個物件,把每個屬性照抄過來。 - 只能利用member function去做overload. - copy constructor: ``` Dog dog1(10,2,10.5); Dog dog2(dog1); //copy constructor ``` - operator+ , <, > overloading: ``` class Dog{ public: Dog(int a){ age = a; } int getAge(){ return age; } Dog operator+ (Dog dogB){ //operator add overloading return Dog(age + dogB.age); } bool operator> (Dog dogB){ //operator add overloading return age > dogB.age; } bool operator< (Dog dogB){ //operator add overloading return age < dogB.age; } private: int age; } void main(){ Dog dog1(10); Dog dog2(20); cout<<"dog 1 + dog 2 = "<<(dog1+dog2).getAge()<<endl; return; } ``` > Output: > dog 1 + dog 2 = 30 - **須注意!!!****std.h**中的必須做operator overloading**特例**: - 如**map與set**等**ordered** structure - 故當儲存在map、set當中的是class object時,在進行insert等動作時,需要進行**排序**(key的**比較**等) - 故需針對該class做 **>、<** 等運算子做**operator overloading** ``` #include "passenger.h" using namespace std; std::map<int,Passenger> passenger_list; Passenger p1(10); passenger_list[10] = p1; //insert需要排序比較 ``` ## Inheritance Chapter 13 - ### 名詞解釋 - **Base** Class vs **Derived** Class - base: 原先就存在的class,比較general。 - derived: 比較specific,繼承base class的特性(是base class的object),但可以有自己獨立的一些特性。 - **Direct** Base Class vs **Indirect** Base Class - direct base: 你爸媽,直系繼承的第一個class - indirect base: 你阿公阿嬤等,直系繼承但是好幾層以上的class - **Single** Inheritance vs **Multiple** Inheritance - single: 只繼承一個base class - multiple: 繼承多個base class,校長同時是"老師"也是"管理者" - ### **Inheritance Mode** - public / private / protected inheritance ``` class Animal{ public: void set_name(string n){ name=n; } private: string name; protected: string weight; class Dog: public Animal{ //public inheritance public: void bark(){} private: string barksound; } class Cat: private Animal{ //private inheritance public: void meow(){} private: string meowsound; } class Bird: protected Animal{ //protected inheritance public: void fly(){} private: string flysound; } } ``` - public/private/protected inheritance 存取權限 ``` class A{ public: method_public; private: method_private; protected: method_protected; } class B: public A{ // method_public: public // method_private: 不可存取 // method_protected: protected } class C: private A{ // method_public: private(可於內部呼叫,不可於外部呼叫) // method_private: 不可存取 // method_protected: private(可於內部呼叫,不可於外部呼叫) } class D: protected A{ // method_public: protected(可於內部呼叫但有接收者,不可於外部呼叫) // method_private: 不可存取 // method_protected: protected(可於內部呼叫但有接收者,不可於外部呼叫) } class E: A{ //默認private inheritance // method_public: private(可於內部呼叫,不可於外部呼叫) // method_private: 不可存取 // method_protected: private(可於內部呼叫,不可於外部呼叫) } ``` - 須注意: derived class也**無法存取**base class的**private member** - 如果是public繼承: - 派生類別與外部**可直接存取**基類**public function** - 派生類別與外部**無法存取**基類的**private function** - **派生類別內部可以直接存取**基類的**protected function**,但若無提供介面給外部,**外部是不可直接存取**基類的protected function - 如何提供介面? - 在派生類別建立public function,外部透過此public function可以存取protected function/variables - **建構子及解構子順序** ``` class A{ private: int age; B b; C c; } class B{} class C{} class D: public A{} A* a = new A(); delete a; D* d = new D(); delete d; ``` > 順序: > 建構B->建構C->建構A > 解構A->解構C->解構B > 建構B->建構C->建構A->建構D > 解構D->解構A->解構C->解構B ``` class A{ public: A(int k){ age = k; } private: int age; } class B: public A { public: B(int a, int b):A(a){ cc = b; } private: int cc; } B* b = new B(27,4); delete b; ``` > 順序: > 建構A->建構B > 解構B->解構A - ### 重要!!! **Base and Derived Class Behavior** ``` #include <iostream> #include <string> #include <stdlib.h> #include <stdio.h> using namespace std; class Base { public: Base() { my_name = 10; key = 'a'; cout<<"create base," << endl; } ~Base() { cout << "destroy base," << endl; } void f1() { cout<<"base f1," << endl; } virtual void f2() { cout<<"base f2" << endl; } private: int my_name; char key; }; class Derived : public Base { public: Derived() { kk = "abc"; cout << "create derived," << endl; } ~Derived() { cout << "destroy derived"<<endl; } void f1() { cout << "derived f1," << endl; } virtual void f2() { cout << "derived f2" << endl; } void f3() { cout << "derived specific function" << endl; } private: string kk; }; int main() { Base* bp = new Base(); //base obj cout << "---" << endl; Base* bd = new Derived(); //base obj with override cout << "---" << endl; Derived* dr = new Derived(); //derived obj cout << "---" << endl; Base* d = bd; //base -> base Base* b = dr; //derived -> base : slicing cout << "---" << endl; //base->derived : will cause problem: //Derived* d = bd; Derived* d = bp; bp->f1(); bp->f2(); bd->f1(); bd->f2(); //bd->f3() : bd is base obj override by derived : no f3 function delete bp; cout << "---" << endl; delete bd; cout << "---" << endl; delete dr; cout << "---" << endl; return 0; } ``` - #### **輸出與結論** ![](https://i.imgur.com/CJH20g6.png) - #### **建立、初始化:** - 情況一: **以Base pointer指向新配置的Derived空間**: - Base* bd = new Derived(); - 建立一個derived obj , **constructor : base->derived**(根據new後面的class) - slicing 成一個base obj,override改寫f2() - 不具有f3()這個derived obj才有的function - 情況二: **Derived 透過slicing跟override可以初始化 Base,反之不行:** - **1. Derived 透過slicing跟override可以初始化 Base Obj**: - ex1. Base* b = bd; - ex2. Base* b = dr; - b會被視作指向一個slicing後的base obj,但是由於override改寫了f2() - **2. 無法透過 Base Obj 初始化 Derived Obj:** - ex1. (compile error!) Derived* d = bd; - ex2. (compile error!) Derived* c = dr; - #### **刪除、釋放記憶體:** - 視作釋放一個base obj, destructor: base - **pointer是指向Base obj的** - ex1. delete b; - ex2. delete bd; - 視作釋放一個derived obj, destructor: derived -> base - **pointer是指向Derived obj的** - delete dr; ## Polymorphism Chapter 14 - ### Polymorphism: - 定義: - 根據接收者是誰決定實際上執行的行為 - 條件須三項吻合: - Inheritance - Overloading - Overriding - ### **Virtual Function**: - 用在polymorphism,會加在base class中,不知道明確如何implement的function上 - 實用此情境時,使用**base class的pointer**來導向base class以及derived class ``` class Operate{ public: virtual void press(){ cout<<"按下開關。"<<endl; } class SW1: public Operate{ public: void press(){ cout<<"按下101大樓內電梯裡的開關"<<endl; } } class SW2: public Operate{ public: void press(){ cout<<"按下101大樓內頂樓陽台的開關"<<endl; } } void turnoff(Operate *Q1){ //一定要用pointer或reference喔! Q1->press(); } Operate Q1; SW1 s1; SW2 s2; turnoff(&Q1); // base class turnoff(&s1); // derived class 1 turnoff(&s2); // derived class 2 } ``` > Output: > 按下開關。 > 按下101大樓內電梯裡的開關 > 按下101大樓內頂樓陽台的開關 - 一旦有pure virtual的function存在,該class即是一個抽象class,不可有該class的instance存在。 - ### Casing - Upcasting vs Downcasting - Upcasting: 把derived class obj指派(assign)給base class obj,但有slicing problem,即遺失derived class才有的特性(善用virtual function避免輸出遺失的特性) ``` class Pet{} class Dog: public Pet{} Pet pet; Dog dog; pet = dog; // upcasting with slicing ``` - Downcasting: 把base class指派給derived class,僅允許使用在pointer+dynamic casting的時候 ``` class Pet{} class Dog: public Pet{} Pet pet = new Dog(); Pet p = new Pet(); Dog dog = new Dog(); //correct one Dog d = dynamic_cast<Dog*>pet; // downcasting should with dynamic_cast //error cases Dog d = pet; // error without dynamic_cast Dog c = dynamic_cast<Dog*>p; //error because the object p point to is actually a Pet class ``` ![image](https://hackmd.io/_uploads/Sy06xtfD6.png)