---
tags: language
---
c++11 Advanced
===
* Learning Resource
* **Inportant: C++ Primer(5th) is the main reference**
* [Clean Architecture](https://www.tenlong.com.tw/products/9789864342945)
* [Standard C++](https://isocpp.org/)
* [Google C++ Style Guide](https://google.github.io/styleguide/cppguide.html#C++_Version)
* [How to Learn the C and C++ Languages: The Ultimate List](https://www.toptal.com/c/the-ultimate-list-of-resources-to-learn-c-and-c-plus-plus)
* [Solve C++ Code Challenges](https://www.hackerrank.com/domains/cpp)
* [在 Linux 下開發 C/C++ 的新手指南](https://medium.com/fcamels-notes/%E5%9C%A8-linux-%E4%B8%8B%E9%96%8B%E7%99%BC-c-c-%E7%9A%84%E6%96%B0%E6%89%8B%E6%8C%87%E5%8D%97-735fcd960b0)
* [C++ application development ( Part 1 — Project structure )](https://medium.com/heuristics/c-application-development-part-1-project-structure-454b00f9eddc)
* [smallpt](https://www.kevinbeason.com/smallpt/)
# c++ Project Structure by CMake
* [Modern CMake](https://cliutils.gitlab.io/modern-cmake/)
* what is CMake:
* an extensible, open-source system that manages the build process in an operating system
* a compiler-independent manner
* simple configuration files placed in each source directory (called CMakeLists.txt files) are used to generate standard build files
* what CMake-like tools for:
* help to build systems
* using CMake-like tool to build system because:
* avoid hard-coding paths
* need to build a package on more than one computer
* want to use CI (continuous integration)
* need to support different OSs
* want to support multiple compilsers
* want to use an IDE, but not all the time
* want to describe how program is structured logically, not flags and commands
* want to use a library
* want to use tools, like Clang-Tidy, to help you code
* want to use a debugger
* why CMake:
* Every IDE supports CMake (or CMake supports that IDE)
* More packages use CMake than any other system.
* installing CMake:
* note: CMake version should be newer than compiler.
* binaries (faster) & a local folder with CMake only:
```bash
$ mkdir -p cmake-3.14 && wget -qO "https://cmake.org/files/v3.14/cmake-3.14.3-Linux-x86_64.tar.gz" | tar --strip-components=1 -xz -C cmake-3.14
$ export PATH=`pwd`/cmake-3.14/bin:$PATH
```
* ```$ sudo apt-get install cmake3```
* OpenCV
* [How to Install OpenCV in Ubuntu 18.04 LTS for C / C++ (Linux)](http://www.codebind.com/linux-tutorials/install-opencv-ubuntu-18-04-lts-c-cpp-linux/)
* ```g++ opencv_hello.cpp -o output `pkg-config --cflags --libs opencv` ```
# Tools for Class Control
## Copy Control
* classes can define constructors, which control what happens when objects of the class type are created
* classes can control what happens when objects of the class type are copied, asigned, moved or destroyed
* classes control these actions through special member functions:
* **copy** & **move constructor**:
* define what happens when a object is initialized from another object of the same type
* **copy-assignment** & **move-assignment operator**:
* define what happens when we assign an object of a class type to another object that same class type
* destructor:
* defines what happens when an object of the class type ceases to exist
### The Copy Construct
* if a constructor is the copy constructor:
* its first parameter is a reference to the class type
* that parameter is almost always a reference to ```const```
* any additional parameters have default values
* example:
```cpp
class Foo{
public:
Foo(); //default constructor
Foo(const Foo&); // copy constructor
};
```
* synthesizes copy constructor:
* when we do not define a copy constructor for a class, the compiler synthesizes on for us
* a copy constructor is synthesized even if we define other constructors
* synthesized copy constructor for some classes prevents us from copying objects of that class type
* synthesized copy constructor memberwise copies the members of its argument into the object being created
* the compiler copy each nonstatic member in turn from the given object into the one being created
# Object-Oriented Programming (OOP)
* [C++物件導向及增進效率程式技巧](http://disp.ee.ntu.edu.tw/class/C++%E7%89%A9%E4%BB%B6%E5%B0%8E%E5%90%91%E5%8F%8A%E5%A2%9E%E9%80%B2%E6%95%88%E7%8E%87%E7%A8%8B%E5%BC%8F%E6%8A%80%E5%B7%A7)
* Object oriented is a programming paradigm(範示)
## 前言
最早的 OO 始於程式設計師察覺到,函式呼叫所設計的堆疊框架(stack frame)可以被移動到一個堆(heap)中,如此能允許函式宣告的局部變數在函式返回後仍存在長一段時間。該函式成了類別的建構函式(constructor),局部變數成了實例變數(instance variable),內部巢狀函式便成了方法。而透過有紀律的使用函式指標,就必然會發現多型性(polymorphism)。
目前物件導向變成一種被廣泛接受的**程式設計範式(Programming Paradigms)**,是希望可以增加軟體的重用性與可維護性...等,其他對應的程式設計範式還有**結構化程式設計**、**函數式程式設計**...等。
## 重新了解物件導向
* 如果去除掉一些譬喻式的說明,改從 OO 的一些特性去了解,並用 C 語言去實作一些特性,會對 OO 有更深的體會(或是說以前實在太淺了XD)
* 基本 struct + function 外的擴充特性:
* 更精細的成員範圍控制-> private, public, protecte ...等
* 複製與延伸原有 struct + function -> 繼承
* 不同物件對應不同行為控制 -> virtual function, vtable
* 相同運算子對應不同型態都可以運作 -> operator overloading
* 物件其相關資源自動釋放 -> reference counting
* 以上 OO 特性的最大好處:透過使用多型,來獲得對於系統中每個原始碼依賴方向的絕對控制力
* EX: 透過抽象工廠產生不容易變動的界面,讓重要的業務邏輯模組絕對獨立於其他週邊模組。
* C 語言都可以實作這些特性,為啥還要 OO 語言?
* OO 語言讓讓多型更加安全便利,儘管付出了很多紙上代價也值得(class, public, private, friend....等)
* 每種語言會迫使使用者用不同面向去思考,C 語言會迫使使用者去思考記憶體配置,OO 語言會迫使使用者去更精細思考物件變數可是範圍與時間...等問題
## 前導知識-抽象
抽象是對具體問題進行概括或統一描述的一個過程,指定一個好抽象可以讓資訊可以更順利的交流,以上講法有點模糊,用一個例子來說明,麵包,可以透過**麵包訂單**這個抽象來對外溝通,顧客只須用指定方式下單,就可以得到麵包,其不用管麵包經過什麼流程製作,也不用管烘焙坊設備有沒有壞或怎麼更換設備...等。而且當**麵包訂單**這個抽象定義得恰當,顧客可以透過這個抽象對不同烘焙坊下訂,如此想換不同麵包只要更換不同烘焙坊就可以。
回到程式用函式來比喻,一個函式是一個抽象,其隱藏了演算流程細節,呼叫函數只需要知道函數名稱並了解 return 的資料,可以不用管內部細節,也可以說函式**封裝**了程式細節,一個 C 語言的 .h檔也可以是一個抽象,當然一個物件導向的類別也算是一個抽象。
來看一下 c++Primer ch7開頭介紹:
```
The fundamental ideas behind classes are data abstraction and encapsulation.
Data abstraction is a programming (and design) technique that relies on the
separation of interface and implementation. The interface of a class consists of
the operations that users of the class can execute. The implementation includes the
class’ data members, the bodies of the functions that constitute the interface, and any
functions needed to define the class that are not intended for general use.
Encapsulation enforces the separation of a class’ interface and implementation. A
class that is encapsulated hides its implementation—users of the class can use the
interface but have no access to the implementation.
A class that uses data abstraction and encapsulation defines an abstract data
type. In an abstract data type, the class designer worries about how the class is
implemented. Programmers who use the class need not know how the type works.
They can instead think abstractly about what the type does.
```
## What is OO?
物件導向觀念不容易解釋,只能用不同角度去說明,而一開始可以用**狀態**與**功能**黏在一起合成一個抽象開始,例如帳戶這個抽象,其包含了一些動作(EX:存/提款)和狀態(金額),當然可以包含更多狀態和功能,換成物件導向語言的話,我們制定出帳戶這個類型應該有什麼功能和狀態後,可以藉由類型產生很多帳戶**物件**,狀態是物件的**資料**,功能是物件的函式或**方法**,而物件的概念讓我們更容易描繪現實問題。
但,資料和函式結合,或是模擬真實世界...等,都沒講到 OO 的本質,目前最常見的解釋方式是用物件導向以下三大特性來說明:
* 封裝(encapsulation)
* 繼承(inheritance)
* 多型(polymorphism)
用這三個名詞來解釋,其含義比較像是指 OO是這三件事以適當的方式合成的產物,或是說 OO語言至少必須支援這三件事,以下先反過來檢視這三個概念:
## 檔案前導知識-標頭檔/實作檔/執行檔
以下內容參閱此連結:[C++ 入門指南 V2.00 - 單元 13 - 設計專屬的標頭檔](https://pydoing.blogspot.com/2015/02/cppg-13.html)
標頭檔 (header file) 的目的在於組織程式原始碼 (source code) 檔案,類別 (class) 、函數 (function) 、常數 (constant) 或特定識別名稱的宣告 (declaration) 都放進標頭檔中,實作則放進實作的程式碼檔案裡,
這樣的目的是因為,
1. 標頭檔(FileName.h)代表的是程式與程式間的介面 (interface) ,通常已經開發完成的程式只需要看介面就能獲悉如何使用,不需要在乎實際實作的細節,
2. 這樣也比較容易維護程式碼,因為使用開發好的程式用前置處理指令 #include 進來就行了。
簡單說,就是把介面跟實作分開,介面無須在每一次測試後都連同修改,有需要修正的地方只需要修正實作的部份即可。
以下簡易 Demo :
首先是標頭檔:**head_demo.h**
```cpp
class Demo{
public:
Demo();
Demo(int);
Demo(int, int);
void set_a(int);
void set_b(int);
int get_a();
int get_b();
int do_something();
private:
int a;
int b;
};
```
接下來是實作檔:**class_demo.cpp**
```cpp
class Demo{
public:
Demo();
Demo(int);
Demo(int, int);
void set_a(int);
void set_b(int);
int get_a();
int get_b();
int do_something();
private:
int a;
int b;
};
```
最後是執行檔:**main_demo.cpp**
```cpp
#include <iostream>
#include "head_demo.h"
using namespace std;
int main(){
Demo t1;
Demo t2(11);
Demo t3(11, 22);
cout << endl;
cout << t1.do_something() << endl;
cout << t2.do_something() << endl;
cout << t3.do_something() << endl;
cout << endl;
return 0;
}
```
編譯時只須把兩個cpp檔一起編譯就可以了:
``` $ g++ class_demo.cpp main_demo.cpp -Wall -std=c++11 -o demo.out```
接下來就正式進入主題吧!!
以下內容參閱此連結:[C++ 入門指南 V2.00 - 單元 13 - 設計專屬的標頭檔](https://pydoing.blogspot.com/2015/02/cppg-13.html)
## 封裝(encapsulation)
封裝是指,一種將抽象性函式介面的實作細節部份包裝、隱藏起來的方法。同時,它也是一種防止外界呼叫端,去存取物件內部實作細節的手段,這個手段是由程式語言本身來提供的。封裝意味著各個層次或者各個獨立系統之間僅僅需要少量的知識來完成各自任務,而且自己本身的某些資訊對於其他個體而言是被隱藏起來的。
引用封裝作為 OO定義的一部分是因為, 封裝是物件導向思維描述世界的一個基礎,OO語言提供簡單又有效的資料和函式(功能)封裝。因此,可以為一組緊密關聯的資料和函式(功能)繪製出一個框,對框外而言,資料是隱藏的,只有一些函式(功能)是已知。這看概念被看做是一個類別的私有資料成員和公有成員函式。但如果一個層次或系統被完全封裝會產生通訊問題,而之前提到的抽象技術就是用來解決這個問題。
封裝不是物件導向所獨有,以下是 C語言展現封裝的簡單範例,point.h的使用者只能呼叫其函式,無權存取 struct Point的成員,也完全不知道 Point資料和函式的實作。
[程式碼參考: Clean Architecture 這一本好書](https://www.tenlong.com.tw/products/9789864342945)
**point.h**
```clike=
struct Point;
struct Point *makePoint(double x, double y);
double distance(struct Point *p1, struct Point *p2);
```
**point.c**
```clike=
/* gcc point.c -o point.o -lm
why 『 -lm 』 :math library must be linked in when building the executable
*/
#include "point.h"
#include <stdlib.h>
#include <stdio.h>
#include <math.h>
struct Point{
double x, y;
};
struct Point* makepoint(double x, double y){
// makepoint 的 return 是個(struct Point type)的指標,
struct Point*
p = malloc(sizeof(struct Point));
p->x = x;
p->y = y;
return p;
}
double distance(struct Point *p1, struct Point *p2){
double dx = p1->x - p2->x;
double dy = p1->y - p2->y;
return sqrt(dx*dx + dy*dy);
}
int main(){
struct Point * p1 = makepoint(10., 10.);
struct Point * p2 = makepoint(20., 20.);
double dis = distance(p2, p1);
printf("distance %f", dis);
return 0;
}
```
以上完美隱藏的封裝在C++中被稍微破壞
**point_cpp.h**
```cpp=
class Point{
public:
Point(double x, double y);
double distance(const Point &p) const;
//在function後面如果加了const的話,就表示它不會修改任何的menber data
private:
double x;
double y;
};
```
```cpp=
#include "point_cpp.h"
#include <math.h>
#include <iostream>
#include <stdio.h>
using namespace std;
//Member initialization in constructors
Point::Point(double x, double y):x(x), y(y){}
double Point::distance(const Point& p) const{
double dx = x - p.x;
double dy = y - p.y;
return sqrt(dx*dx + dy*dy);
}
```
上面範例中,使用者知道 Private的資料,雖然無法存取,但封裝已經被破壞,而Java、 C#廢除了標頭/實作分拆,更弱化了封裝,其無法將一個類別的宣告(declarration)和定義(definition)分離。以上可以說明物件導向語言沒特別強制性封裝。
## 繼承(Inheritance)
繼承就是在一個封閉範圍內重新宣告一組變數和函式,用在物件的話就是子類別(派生類)物件擁有其基礎類別(父類別)相同的屬性和方法,只是要注意子類別一樣不能存取父類別 private 存取權限之成員(但是 protected 成員可以)。同樣,繼承功能也不是 OO 語言獨有的好處,以下是用 C 語言模擬的繼承。
* 先定義第一個類型:Point
**point.h**
```clike
#include <stdlib.h>
#include <stdio.h>
#include <math.h>
struct Point;
struct Point* makePoint(double x, double y);
double distance (struct Point *p1, struct Point *p2);
struct Point{
double x, y;
};
struct Point* makepoint(double x, double y){
struct Point*
p = malloc(sizeof(struct Point));
p->x = x;
p->y = y;
return p;
}
double distance(struct Point *p1, struct Point *p2){
double dx = p1->x - p2->x;
double dy = p1->y - p2->y;
return sqrt(dx*dx + dy*dy);
}
```
* 定義第二個類型:NamedPoint
**namedPoint.h**
```clike=
#include <stdlib.h>
struct NamedPoint;
struct NamedPoint{
double x, y;
char* name;
};
struct NamedPoint* makeNamedPoint(double x, double y, char* name){
struct NamedPoint* p = malloc(sizeof(struct NamedPoint));
p->x = x;
p->y = y;
p->name = name;
return p;
}
void setName(struct NamedPoint* np, char* name){
np->name = name;
}
char* getName(struct NamedPoint* np){
return np->name;
}
```
* 因為 NamedPoint類型是 Point類型的純超集合,而且其欄位順序也與相同,因此我們可以將NamedPoint類型的變數,轉成 Point的類型,並做運算,這就有點類似子類別(NamedPoint)繼承父類別(Point)。
**main.c**
```clike=
#include "point.h"
#include "namedPoint.h"
#include <stdio.h>
int main(int ac, char** av){
struct NamedPoint* origin = makeNamedPoint(0.0, 0.0, "origin");
struct NamedPoint* upperRight = makeNamedPoint(1.0, 1.0, "upperRight");
printf("distance=%f\n",
distance(
// key: can convert NamedPoint type to Point type!!
(struct Point*) origin,
(struct Point*) upperRight
));
}
```
雖然C語言可以模擬繼承,但是 OO語言的繼承讓資料結構的偽裝變得更加方便,並且可以過展到多重繼承。
## 多型(Polymorphism)
多型是指基礎類別中定義的屬性或行為被子類別繼承後,可以具有**不同**的資料類型或表現行為。例如**動物**這個物件有個**移動**的行為,不同的特定動物繼承**動物**後,其**移動**的表現可以是爬、飛、游...等。
當然在 OO語言出現以前就已經有模擬多型的技巧了,但這裡要用到指標函數的觀念,也就是在 function 那一主題的內容!
來看一個實際的例子,考慮一個簡單的 C語言的 **copy**函式:
```clike=
#inlcude <stdio.h>
void copy(){
int c;
while ((c=getchar()) != EOF)
putchar(c);
}
```
一般 getchar()函式是從 STDIN中讀取資料,但電腦設備這麼多,需要怎麼做才能將任務交付給讀取字元的設備驅動程式呢?
為此,有些 UNIX 作業系統要求每個IO設備驅動程式必須提供5個標準函式:open、close、read、write 和 seek,現在定義一個 FILE 資料結構來包含5個函式指標,
```clike=
struct FILE{
void (*open)(char* name, int mode);
void (*close)();
int (*read)();
void (*write)(char);
void (*seek)(long index, int mode);
};
```
主控台(console)的 IO 驅動程式將定義這些函式,並透過函式指標將位址載入到 FILE 資料結構,如下所示:
```cpp
#include "file.h"
void open(char*, name, int mode){/*...*/}
void close(){/*...*/}
int read(){/*...*/}
void write(char c){/*...*/}
void seek(long index, int mode){/*...*/}
struct FILE console = {open, close, read, write, seek};
```
現在,如果 STDIN 被定義為 FILE*, 並且指向了 console資料結構,那麼getchar()就可以如下實作:
```clike=
extern struct FILE* STDIN;
int gerchar(){
return STDIN->read();
}
```
在以上的架構下,如果我們更換了IO設備,最一開始的copy()函式甚至不用重新編譯就可以運作,IO 驅動程式已經變成了copy()外掛(plugin)了。
## OO 語言優勢結論
在結構典範中,函式流程和原始碼依賴方向是一致且單向的,以下是一般的流程與程式碼依賴關係示意圖,

但是現在透過 OO 語言的編寫,只要插入一個**介面(Interface)**,就可以把控制流程跟原始碼依賴關係分開,如下圖:

原本 HL1 和 ML1 的程式碼依賴關係和呼叫流程是一樣的,現在透過一個介面,ML1 的程式碼依賴關係變成反向,也就是說透過 OO 語言,程式設計師可以絕對控制系統中所有原始碼依賴的方向,如此 plugin 的設計就可以產生,任何獨立系統都可以獨立進行部署(Deployed independently),進而可以獨立開發。
最終,總結 OO 語言的特點,就是
**OO 透過使用多型,來獲得對於系統中每個原始碼依賴方向的絕對控制力**,其允許建立一個 plugin 架構,其中包含策略的高層級模組獨立於包含細節的低層級模組,低層級的細節被放到 plging 模組當中,並獨立於高層模組來進行佈署和開發。
## 多形相關知識:
透過以上討論,應該可以感受到如果不深入探討多型,那麼學習物件導向根本就是浪費時間,因為物件導向最大威力就是透過多形架構出 plugin 系統架構。
* [虛有其表的介面](https://openhome.cc/Gossip/JavaEssence/Interface.html)
* [說明:C++虛擬函式(Virtual function)](https://openhome.cc/Gossip/CppGossip/VirtualFunction.html)
* [書本提到的 class, interface, abstract](http://nwpie.blogspot.com/2017/05/3-class-interface-abstract.html)
* Note:想了解的主題:
* 探討編譯器是怎麼實作 dynamic bindings 及多型
* C++ compiler 如何用Function Pointer實作 dynamic bindings