程序導向的C語言也能實作物件導向?C語言要如何模擬類別?C struct與C++ struct與C++ class這三者有什麼差別?類別中有哪六個特殊函式?初始化和賦值有什麼差別?內嵌函式和巨集很像?物件居然可以拿來當成函式?為什麼C++宣告無參數物件時,不用加()?
還是很困惑嗎?文章裡有答案喔~😎
一、類別
(一)物件導向
- 封裝 (encapsulation):改善C語言的封裝與權限問題,封裝完整的話,有利於debug,讓錯誤限制在某一個區塊。
- 繼承 (inheritance):重複使用程式碼。
- 多型 (polymorphism):可以解決switch的問題。同樣函式名與參數,即同樣的函式簽章 (signature),能給出不同的定義。不用動別人的原始碼就可以改變別人函式的功能。
(二)C++ 類別 (class)與結構 (struct)
C++的struct和class兩者幾乎沒有差別,但實務上會希望有所區分。
C++ struct | C++ class |
預設public權限 | 預設private權限 |
預設public繼承 | 預設private繼承 |
不能用在template | 可用在template |
C++ 的 struct 和 class都可以:
- 宣告成員資料
- 宣告成員函式
- 繼承、多型、建構子、解構子、interface
C++物件把data和operations分開,class = data type (data member) + operation (member function)。
更多存取權限與繼承模式,可以參考[筆記]繼承模式與存取權限|C++
(三)C語言模擬類別 (class)
但C struct和C++ struct就不太一樣,C struct可以(1)宣告成員變數,而(2)成員函式和(3)物件導向的東西都不能使用。不過C仍可使用struct模擬class,C struct要用指向函式的指標來代替成員函式,所以C struct的實例 (instances)中,除了有成員資料,還會有很多的指標變數來儲存函式;而C++ class使用this指標去指向成員函式,以達到每個實例共用同一套成員函式的目的,因此每個class實例只會存放成員資料,故C++ class實例的大小就會比C struct實例來得小。
使用C struct模擬C++ class,struct client_t有四個變數,三個是資料,第四個是指向函式的指標 程式碼來源 |
如果想要C struct實例共享同一套函式,則可以直接使用extern,使其他檔案都可以這一套函式,對C struct實例做操作。
物件 (object)、實例 (instance)、變數 (memory allocation),三者概念相近。Struct使用dot (.)存取資料,Class也是使用dot (.),但如是指標,則要使用箭號(->)或start (*) + dot (.)。
二、特殊成員函式
- 預設建構子 (default constructor)
- 解構子 (destructor)
- 複製建構子 (copy constructor)
- 複製運算子 (copy assignment operator)
- 移動建構子 (move constructor)
- 移動運算子 (move assignment operator)
(一)建構子 (constructor)
C語言中常會忘記初始化就直接使用,這樣會出現一些神奇的問題,所以C++有預設的建構子,就可以避免這樣的問題。C++建構子會幫你自動初始化變數,避免用到未初始化的變數。
class::constructor (int a, int b)
:data1(a), data2(b) // Initialization,使用初始列較有效率
{ data1 = a; data2 = b; } // Assignment,較沒效率
1. 初始化 (Initialization)
2. 賦值 (Assignment)
3. 常數變數 (Constant variable)
傳遞參數是初始化還是賦值呢?是初始化,因為傳遞參數時,我們可以使用常數變數,而常數變數只能初始化一次,且不能做賦值 (assignment),故是初始化的動作。如:
- 傳遞參數:int func(const int a);
- const int c = 1; //合法
- const int d; d = 2 // 非法,因為d已經被初始化為0,不能再賦值為2
(二)其他特殊函式
三、內嵌函式 (Inline function)
內嵌函式和巨集 (macro)很像,都是文字取代機制,可以降低函式指標跳轉的時間。因此內嵌函式可以取代巨集的函式定義的功能,且內嵌函式可以避免巨集的一些問題,尤其是++或--的時候,不過缺點就是記憶體用量變多。
- 內嵌函式:
- inline int max (init a, int b){ return a>b ? a : b; }
- int m = max(++x, ++y); // 這邊x或y只會被加一次
- 巨集:
- #define max(a, b) a>b ? a : b
- int m = ++x > ++y ? ++x : ++y; // 這邊x或y會被加二次
四、函式物件 (functional object)
- struct FO{ int operator()() { return 100;} };
- C++ struct幾乎和C++ class一樣,多載運算子()
- Fo fo; std: :cout << fo() << std::endl;
- Call fo.operator()()
- 呼叫fo物件的多載運算子()
- std::cout << FO()() << std::endl;
- Call default constructor == FO().operator()()
- 呼叫預設建構子的多載運算子()
- FO()是在呼叫default constructor,第二個()是在呼叫多載運算子()
- std: :cout << FO{}() << std: :endl;
- Call default constructor, {} is initializer, == FO{}.operator()()
- 一樣是呼叫預設建構子的多載運算子(),只不過這裡是使用初始化列{}來初始化物件。
C++11的lambda表達式,由於編譯器會將lambda表達式轉成函式物件,故也是一種函式物件,只不過它是一種匿名的函式。
為什麼C++在宣告沒有引數的物件時,不能加()?因為C++會無法分辨className()為建構子還是函式物件這算是C++先天的缺陷。因此C++認為className()為函式物件,而className為建構子。
留言
張貼留言