學習筆記之C++入門到精通(名師教學·手把手教會)【職座標】_騰訊課堂

C++入門到精通(名師教學·手把手教會)【職座標】_騰訊課堂java

https://ke.qq.com/course/101465#term_id=100105503ios

https://github.com/haotang923/ke.qq.com.cppc++

內聯函數

  • 函數調用須要創建棧內存環境,進行參數傳遞,併產生程序執行轉移,這些工做都須要時間開銷。
  • C++提供inline函數,減小函數調用的成本。編譯器看到inline後,爲該函數建立一段代碼,以便在後面每次碰到該函數的調用都用同一段代碼來替換。
  • 內聯函數能夠在一開始僅聲明一次。
  • 內聯函數必須在調用以前被聲明或定義,由於它的代碼必須在被替換以前已經生成被替換的代碼。
  • 內聯函數中,不能有複雜結構控制語句如swtich/while/for。不然編譯將該函數視爲普通函數那樣產生函數調用代碼。
  • 遞歸函數不能做爲內聯函數。
  • 內聯函數只適合於1-5行小函數。對於較長的函數,函數調用和返回的開銷相對來講微不足道,也不必用內聯函數實現。 
 1 //
 2 //  main.cpp
 3 //  LeetCode
 4 //
 5 //  Created by Hao on 2017/3/16.
 6 //  Copyright © 2017年 Hao. All rights reserved.
 7 //
 8 
 9 #include <iostream>
10 using namespace std;
11 
12 #define MAX(a, b) ((a) > (b) ? (a) : (b))
13 
14 #define WRONGSQUARE(x) (x * x)
15 #define SQUARE(x) ((x) * (x))
16 
17 inline int max(int a, int b)
18 {
19     return a > b ? a : b;
20 }
21 
22 int main()
23 {
24     int a = 55, b = 4;
25     
26     int c = max(a ++, b);
27     
28     cout << "c = " << c << endl;
29     cout << "a = " << a << endl;
30 
31     a = 55;
32     
33     int d = MAX(a ++, b); // a ++ > b ? a ++ : b;
34     
35     cout << "d = " << d << endl;
36     cout << "a = " << a << endl;
37 
38     int e = SQUARE(2 + 3); // ((2 + 3) * (2 + 3))
39     
40     cout << "e = " << e << endl;
41     
42     int f = WRONGSQUARE(2 + 3); // (2 + 3 * 2 + 3)
43     
44     cout << "f = " << f << endl;
45     
46     return 0;
47 }
View Code
  • 結果能夠看出宏使用中出現了不可預期的結果,而內聯函數不會。
c = 55
a = 56
d = 56
a = 57
e = 25
f = 11
Program ended with exit code: 0
View Code

默認參數的函數

  • 若是一個函數有多個默認參數,則行參分佈中,默認參數應從右往左逐漸定義。而當調用參數時,只能從右往左匹配參數。
 1 //
 2 //  main.cpp
 3 //  LeetCode
 4 //
 5 //  Created by Hao on 2017/3/16.
 6 //  Copyright © 2017年 Hao. All rights reserved.
 7 //
 8 
 9 #include <iostream>
10 using namespace std;
11 
12 void foo(int i, int j = 5, int k = 10);
13 //void foo(int, int = 5, int = 10);
14 
15 int main()
16 {
17     foo(20);
18     foo(20, 30);
19     foo(20, 30, 40);
20     
21     return 0;
22 }
23 
24 void foo(int i, int j, int k)
25 {
26     cout << i << " " << j << " " << k << endl;
27 }
View Code
  • 結果
20 5 10
20 30 10
20 30 40
Program ended with exit code: 0
View Code

函數重載

  • 相同的函數名,可是行參個數或類型不一樣
  • 編譯器不以行參名,返回值來區分
  • 預約義的宏
    • https://msdn.microsoft.com/zh-cn/library/b0084kay.aspx
 1 //
 2 //  main.cpp
 3 //  LeetCode
 4 //
 5 //  Created by Hao on 2017/3/16.
 6 //  Copyright © 2017年 Hao. All rights reserved.
 7 //
 8 
 9 #include <iostream>
10 using namespace std;
11 
12 int square(int x)
13 {
14     cout << __FILE__ << " " << __LINE__ << " " << __func__ << endl; // File / Line / Function
15     return x * x;
16 }
17 
18 double square(double x)
19 {
20     cout << __FILE__ << " " << __LINE__ << " " << __func__ << endl; // File / Line / Function
21     return x * x;
22 }
23 
24 int main()
25 {
26     cout << "square(10)\n" << square(10) << endl;
27     cout << "suqare(1.1)\n" << square(1.1) << endl;
28     
29     return 0;
30 }
View Code
  • 結果注意用到預約義宏__FILE__,__LINE__,__func__來輸出文件,行數和函數
square(10)
/Users/hao/PROJECTS/LeetCode/LeetCode/main.cpp 14 square
100
suqare(1.1)
/Users/hao/PROJECTS/LeetCode/LeetCode/main.cpp 20 square
1.21
Program ended with exit code: 0
View Code
  • C語言頭文件中的extern "C"
 1 //
 2 //  Header.h
 3 //  LeetCode
 4 //
 5 //  Created by Hao on 2017/12/3.
 6 //  Copyright © 2017年 Hao. All rights reserved.
 7 //
 8 
 9 //常見的C語言頭文件格式
10 #ifndef Header_h
11 #define Header_h
12 
13 #ifdef __cplusplus
14 extern "C" {
15 #endif
16 
17 // C語言的函數在C++中調用
18 void sample();
19 
20 #ifdef __cplusplus
21 }
22 #endif
23 
24 #endif /* Header_h */
View Code

函數模版

  • 對於具備各類參數類型,相同個數,相同順序的同一函數(重載函數),若是用宏定義來寫,則它不能檢查數據類型,損害了類型安全性 。
  • 用模版能夠減小代碼量。
 1 //
 2 //  main.cpp
 3 //  LeetCode
 4 //
 5 //  Created by Hao on 2017/3/16.
 6 //  Copyright © 2017年 Hao. All rights reserved.
 7 //
 8 
 9 #include <iostream>
10 using namespace std;
11 
12 template <typename T>
13 T fAbs(T x)
14 {
15     return x < 0 ? -x : x;
16 }
17 
18 int main()
19 {
20     int n = -5;
21     double d = -5.5;
22     
23     cout << fAbs(n) << endl;
24     cout << fAbs(d) << endl;
25     
26     return 0;
27 }
View Code
5
5.5
Program ended with exit code: 0
View Result

類、對象和封裝

  • struct安全性很差,任何人都能訪問
  • class類不只能夠保護數據,還能夠提供成員函數操做數據 
  • 類中定義的成員函數通常位內聯函數,即便沒有用inline標示
  • this指針表明當前對象佔用內存空間的地址
  • OOP的三大特性封裝(encapsulation)、多態(polymorphism)、繼承(inheritance)

構造函數與析構函數

  • 只要類定義了一個構造函數,C++就再也不提供默認的構造函數。
  • 與變量定義相似,在用默認構造函數建立對象時,若是建立的是全局對象或靜態對象,則對象的值爲0,不然對象值是隨機的。
  • 構造函數的一個特殊之處是它沒有返回類型,函數體中也不容許返回值,但能夠有無值返回語句「return;」。
  • 若是建立一個5個元素的對象數組,則構造函數會被調用5次。
  • 析構函數只有一個,不能重載
 1 //
 2 //  Student.hpp
 3 //  LeetCode
 4 //
 5 //  Created by Hao on 2017/12/11.
 6 //  Copyright © 2017年 Hao. All rights reserved.
 7 //
 8 
 9 #ifndef Student_hpp
10 #define Student_hpp
11 
12 #include <iostream>
13 using namespace std;
14 
15 class Student
16 {
17 public:
18     Student(int id = 0);
19     ~Student();
20 
21     const int getID() const;
22     void setID(int id);
23     const int getScore() const;
24     void setScore(int score);
25     
26 private:
27     int m_id;
28     int m_score;
29 };
30 
31 inline const int Student::getID() const
32 {
33     return m_id;
34 }
35 
36 inline void Student::setID(int id)
37 {
38     m_id = id;
39 }
40 
41 inline const int Student::getScore() const
42 {
43     return m_score;
44 }
45 
46 inline void Student::setScore(int score)
47 {
48     m_score = score;
49 }
50 
51 #endif /* Student_hpp */
Student.hpp
 1 //
 2 //  Student.cpp
 3 //  LeetCode
 4 //
 5 //  Created by Hao on 2017/12/11.
 6 //  Copyright © 2017年 Hao. All rights reserved.
 7 //
 8 
 9 #include "Student.hpp"
10 #include <iostream>
11 using namespace std;
12 
13 Student::Student(int id)
14 : m_score(0), m_id(id)
15 {
16     cout << "Student Constructor" << endl;
17 }
18 
19 Student::~Student()
20 {
21     cout << "Student destructor" << endl;
22 }
Student.cpp
 1 //
 2 //  main.cpp
 3 //  LeetCode
 4 //
 5 //  Created by Hao on 2017/3/16.
 6 //  Copyright © 2017年 Hao. All rights reserved.
 7 //
 8 
 9 #include <iostream>
10 #include "Student.hpp"
11 using namespace std;
12 
13 int main()
14 {
15 //    {
16     class Student std(10);
17     
18     cout << std.getID() << endl;
19     cout << std.getScore() << endl;
20 //    }
21     cout << "return from main()" << endl;
22     
23     return 0;
24 }
main.cpp
Student Constructor
10
0
return from main()
Student destructor
Program ended with exit code: 0
View Result

static類成員

  • 在static成員函數中不能使用this指針,由於this指針是屬於對象的,而static成員函數是屬於類的,不屬於某一對象。
  • 即便沒有實例化類的對象,static數據成員和成員函數仍然可使用。
  • static成員的名字在類的做用域中,所以能夠避免與其餘的類的成員或者全局對象名字衝突。
  • 能夠實施封裝,static成員能夠是私有成員,而全局對象不能夠。

動態內存分配

  • C語言的動態內存分配:malloc/free函數
  • 內存區域
    • data area:全局變量、靜態數據、常量 
    • code area:全部類成員函數和非成員函數代碼
    • stack area:爲運行函數而分配的局部變量、函數參數、返回數據、返回地址等
    • heap area:動態內存分配區
  • C++的運算符new/delete和malloc/free區別
    • 在堆上生成對象,須要自動調用構造函數。new能夠作到,而malloc不行。
    • 在堆上生成的對象,在釋放時須要自動調用析構函數。delete能夠,而free不行。
    • new[]/delete[]生成和釋放動態數組
    • new/delete,new[]/delete[]和malloc/free須要配對使用
    • new/delete是運算符,而malloc/free是函數調用
 1 //
 2 //  main.cpp
 3 //  LeetCode
 4 //
 5 //  Created by Hao on 2017/3/16.
 6 //  Copyright © 2017年 Hao. All rights reserved.
 7 //
 8 
 9 #include <iostream>
10 #include <cstdlib> // malloc/free
11 using namespace std;
12 
13 class Test
14 {
15 public:
16     Test(int val = 0)
17     : m_val(val)
18     {
19         cout << "Test" << endl;
20     }
21     
22     ~Test()
23     {
24         cout << "~Test" << endl;
25     }
26     
27 private:
28     int m_val;
29 };
30 
31 int main ()
32 {
33     {
34         Test a;                 // "Test"
35     }                           // End of scope : "~Test"
36     cout << "end of }" << endl;
37     
38     Test *pVal = new Test();    // "Test"
39     delete pVal;                // "~Test"
40     pVal = nullptr;
41     
42     int *p = (int *)malloc(sizeof(int));
43     free(p);
44     p = nullptr;
45     
46     Test *pArray = new Test[2]; // twice "Test"
47     
48     delete[] pArray;            // twice call of destructor "~Test"
49     //delete pArray;            // memory leak
50     
51     pVal = new Test(10);        // "Test"
52     delete pVal;                // "~Test"
53     
54     return 0;
55 }
View Code
Test
~Test
end of }
Test
~Test
Test
Test
~Test
~Test
Test
~Test
Program ended with exit code: 0
View Result

拷貝構造函數

  • 當將該類型的對象傳遞給函數或從函數返回該類型的對象時,將隱式的調用拷貝構造函數。
  • 若是一個類沒有定義拷貝構造函數,編譯器會默認提供拷貝構造函數。
  • 編譯器提供的默認拷貝構造函數的行爲
    • 執行逐個成員初始化,將新對象初始化爲原對象的副本。
    • 「逐個成員」,指的是編譯器將現有對象的每一個非static成員,依次複製到正在建立的對象。
  • 爲何C++要定義拷貝構造函數淺拷貝:建立對象p2時,對象p1被複制給了p2,但資源並未複製。所以,p1和p2指向同一個資源。
    • 兩個對象擁有統一資源,引發問題
  • 下例演示了淺拷貝帶來的問題。使用默認的拷貝構造函數建立的對象p2,只是淺拷貝對象p,因此它們指向同一塊內存空間。而程序結束時,依次析構p2,p3,p。因爲析構p2時已經將內存空間釋放,因此析構p時出錯「pointer being freed was not allocated」。
 1 //
 2 //  person.hpp
 3 //  LeetCode
 4 //
 5 //  Created by Hao on 2017/12/26.
 6 //  Copyright © 2017年 Hao. All rights reserved.
 7 //
 8 
 9 #ifndef person_hpp
10 #define person_hpp
11 
12 class Person
13 {
14 public:
15     Person(char * pName);
16     ~Person();
17     /*
18      Person(const Person &s);
19      Person& operator=(const Person &other);
20     */
21     
22     void Print();
23     
24 private:
25     char *name;
26 };
27 
28 
29 #endif /* person_hpp */
person.hpp
 1 //
 2 //  person.cpp
 3 //  LeetCode
 4 //
 5 //  Created by Hao on 2017/12/26.
 6 //  Copyright © 2017年 Hao. All rights reserved.
 7 //
 8 
 9 #include "person.hpp"
10 
11 #include <iostream>
12 #include <cstring>
13 using namespace std;
14 
15 Person::Person(char *pN)
16 {
17     if (pN != nullptr) {
18         cout << "Constructing " << pN << " --->" << endl;
19 
20         int len = strlen(pN) + 1;
21         name = new char[len];
22         cout << "name = " << static_cast<void *>(name) << "\n" << endl;
23         memset(name, 0, len);
24         strcpy(name, pN);
25     } else {
26         name = nullptr;
27     }
28 }
29 
30 Person::~Person()
31 {
32     cout << "Destrcuting Person --->" << endl;
33     
34     if (name != nullptr) {
35         Print();
36         delete [] name;
37         name = nullptr;
38     }
39 }
40 
41 void Person::Print()
42 {
43     cout << "pName = " << static_cast<void *>(name) << "\n" << endl;
44 }
person.cpp
 1 //
 2 //  main.cpp
 3 //  LeetCode
 4 //
 5 //  Created by Hao on 2017/3/16.
 6 //  Copyright © 2017年 Hao. All rights reserved.
 7 //
 8 
 9 #include <iostream>
10 using namespace std;
11 
12 #include "person.hpp"
13 
14 int main ()
15 {
16     Person p("Joe");
17     Person p3("Tom");
18     
19     Person p2 = p; // 淺拷貝:使用編譯器提供的默認的拷貝構造函數,指向同一塊內存空間。致使析構時出現問題,同一塊內存空間被析構兩次。
20     
21     cout << "Print p --->" << endl;
22     p.Print();
23     
24     cout << "Print p2 --->" << endl;
25     p2.Print();
26     
27     return 0;
28 }
main.cpp
// 構造p
Constructing Joe --->
name = 0x100429ab0

// 構造p3
Constructing Tom --->
name = 0x100429d80

Print p --->
pName = 0x100429ab0

Print p2 --->
pName = 0x100429ab0

// 析構p2
Destrcuting Person --->
pName = 0x100429ab0

// 析構p3
Destrcuting Person --->
pName = 0x100429d80

// 析構p
Destrcuting Person --->
pName = 0x100429ab0

LeetCode(2399,0x100395340) malloc: *** error for object 0x100429ab0: pointer being freed was not allocated
*** set a breakpoint in malloc_error_break to debug
(lldb) 
View Result
  • 下例演示了深拷貝。注意到拷貝構造函數和重載賦值運算符裏都將對象指向一塊新的內存空間,因此析構時沒有出現同一塊內存空間被釋放屢次的問題。
 1 //
 2 //  person.hpp
 3 //  LeetCode
 4 //
 5 //  Created by Hao on 2017/12/26.
 6 //  Copyright © 2017年 Hao. All rights reserved.
 7 //
 8 
 9 #ifndef person_hpp
10 #define person_hpp
11 
12 class Person
13 {
14 public:
15     Person(char * pName);
16     ~Person();
17     Person(const Person &s);
18     Person& operator= (const Person &other);
19     
20     void Print();
21     
22 private:
23     char *name;
24 };
25 
26 
27 #endif /* person_hpp */
person.hpp
 1 //
 2 //  person.cpp
 3 //  LeetCode
 4 //
 5 //  Created by Hao on 2017/12/26.
 6 //  Copyright © 2017年 Hao. All rights reserved.
 7 //
 8 
 9 #include "person.hpp"
10 
11 #include <iostream>
12 #include <cstring>
13 using namespace std;
14 
15 Person::Person(char *pN)
16 {
17     if (pN != nullptr) {
18         cout << "Constructing " << pN << " --->" << endl;
19 
20         int len = strlen(pN) + 1;
21         name = new char[len];
22         cout << "name = " << static_cast<void *>(name) << "\n" << endl;
23         memset(name, 0, len);
24         strcpy(name, pN);
25     } else {
26         name = nullptr;
27     }
28 }
29 
30 Person::~Person()
31 {
32     cout << "Destrcuting Person --->" << endl;
33     
34     if (name != nullptr) {
35         Print();
36         delete [] name;
37         name = nullptr;
38     }
39 }
40 
41 Person::Person(const Person &p)
42 {
43     cout << "Copy Constructor of Person --->" << endl;
44     
45     if (p.name != nullptr) {
46         int len = strlen(p.name) + 1;
47         name = new char[len];
48         cout << "name = " << static_cast<void *>(name) << "\n" << endl;
49         memset(name, 0, len);
50         strcpy(name, p.name);
51     } else {
52         name = nullptr;
53     }
54 }
55 
56 // 注意「operator= ()」須要留一個空格,不然出錯
57 Person& Person::operator= (const Person &other)
58 {
59     cout << "operator= --->\n" << endl;
60     
61     // 防止自賦值
62     if (&other == this) {
63         return *this;
64     }
65     
66     if (name != nullptr) {
67         delete [] name;
68         name = nullptr;
69     }
70     
71     if (other.name != nullptr) {
72         int len = strlen(other.name) + 1;
73         name = new char[len];
74         memset(name, 0, len);
75         strcpy(name, other.name);
76     } else {
77         name = nullptr;
78     }
79     
80     return *this;
81 }
82 
83 void Person::Print()
84 {
85     cout << "pName = " << static_cast<void *>(name) << "\n" << endl;
86 }
person.cpp
 1 //
 2 //  main.cpp
 3 //  LeetCode
 4 //
 5 //  Created by Hao on 2017/3/16.
 6 //  Copyright © 2017年 Hao. All rights reserved.
 7 //
 8 
 9 #include <iostream>
10 using namespace std;
11 
12 #include "person.hpp"
13 
14 int main ()
15 {
16     Person p("Joe");
17     Person p3("Tom");
18     
19     Person p2 = p; // 深拷貝:自定義拷貝構造函數,指向不一樣內存空間。析構時沒有問題。
20     
21     cout << "Print p --->" << endl;
22     p.Print();
23     
24     cout << "Print p2 --->" << endl;
25     p2.Print();
26     
27     p2 = p3; // 重載賦值運算符
28     
29     cout << "Print p3 --->" << endl;
30     p3.Print();
31     
32     cout << "Print p2 --->" << endl;
33     p2.Print();
34 
35     return 0;
36 }
main.cpp
// 構造p
Constructing Joe --->
name = 0x103105370

// 構造p3
Constructing Tom --->
name = 0x1031053f0

// 構造p2
Copy Constructor of Person --->
name = 0x10310a520

Print p --->
pName = 0x103105370

Print p2 --->
pName = 0x10310a520

// 賦值給p2
operator= --->

Print p3 --->
pName = 0x1031053f0

Print p2 --->
pName = 0x100608e20

// 析構p2
Destrcuting Person --->
pName = 0x100608e20

// 析構p3
Destrcuting Person --->
pName = 0x1031053f0

// 析構p
Destrcuting Person --->
pName = 0x103105370

Program ended with exit code: 0
View Result
  • 若是想禁止一個類的拷貝構造,須要將拷貝構造函數聲明爲private
  • 什麼時候須要定義拷貝構造函數 
    • 類數據成員有指針
    • 類數據成員管理資源(如打開一個文件)
    • 類須要析構函數來釋放資源

const關鍵字

  • const限定指針類型const數據成員必須使用成員初始化列表進行初始化
    • const出如今*左邊,表示被指物是常量
    • const出如今*右邊,表示指針自身是常量
  • const成員函數
    • 類接口清晰,肯定哪些函數能夠修改數據成員
  • 使用const提升函數的健壯性下例演示了const修飾符做用,const數據成員初始化,const成員函數以及引用傳遞和值傳遞。
    • 用引用傳遞替代值傳遞:減小實參爲對象時,拷貝構造/析構對象的代價。
    • 控制使用指針和引用傳遞的實參被意外修改
 1 //
 2 //  main.cpp
 3 //  LeetCode
 4 //
 5 //  Created by Hao on 2017/3/16.
 6 //  Copyright © 2017年 Hao. All rights reserved.
 7 //
 8 
 9 #include <iostream>
10 using namespace std;
11 
12 class Student
13 {
14 public:
15     Student(int id = 0)
16         : m_id(id) // Must use initialization list to initialize the const member
17     {
18         cout << "Student constructor --->\n" << endl;
19         
20         // ERROR : Cannot assign to non-static data member 'm_id' with const-qualified type 'const int'
21         //m_id = id;
22     }
23     
24     ~Student()
25     {
26         cout << "Student destructor --->\n" << endl;
27     };
28     
29     Student(Student &other)
30         : m_id(other.m_id)
31     {
32         cout << "Student copy constructor --->\n" << endl;
33     }
34     
35     inline int getID() const
36     {
37         return m_id;
38     }
39     
40 private:
41      const int m_id;
42 };
43 
44 void foo(Student stu)
45 {
46     cout << __func__ << "\n" << endl;
47 }
48 
49 void bar(const Student &stu)
50 {
51     cout << __func__ << "\n" << endl;
52 }
53 
54 int main ()
55 {
56     const int a = 1;
57 
58     // ERROR : Cannot assign to variable 'a' with const-qualified type 'const int'
59     //a = 3;
60     
61     int b = 0;
62     
63     // const value
64     const int *p = &a;
65 
66     p = &b;
67     b = 3;
68     
69     cout << "p = " << *p << endl;
70     
71     // ERROR : Read-only variable is not assignable
72     //*p = 1;
73     
74     // const pointer
75     int * const p2 = &b;
76     
77     // ERROR : Cannot assign to variable 'p2' with const-qualified type 'int *const'
78     //p2 = &a;
79     
80     *p2 = 2;
81     
82     cout << "p2 = " << *p2 << "\n" << endl;
83     
84     {
85         Student john(1001);
86         
87         cout << "Call foo(Student stu)\n" << endl;
88         
89         foo(john);
90         
91         cout << "Call bar(const Student &stu)\n" << endl;
92         
93         bar(john);
94     }
95 
96     cout << "Return from main" << endl;
97     
98     return 0;
99 }
View Code
p = 3
p2 = 2

// 構造對象
Student constructor --->

Call foo(Student stu)

// 值傳遞時對行參調用拷貝構造函數
Student copy constructor --->

foo

// 函數調用完畢,對行參調用析構函數
Student destructor --->

// 引用傳遞不會調用構造析構行參
Call bar(const Student &stu)

bar

// 對象生命週期結束,析構對象
Student destructor --->

Return from main
Program ended with exit code: 0
View Result

友元函數與友元類

  • 友元機制容許一個類對其非公有成員對訪問授予指定的函數或類。友元關係時授予的,必須顯示聲明時友元
    • 友元的聲明以friend開始
    • 只能出如今類定義的內部
    • 能夠出如今類中的任何地方,不受其聲明出現部分的訪問控制(public/private/protect)影響 
  • 友元關係是不對稱的
  • 友元會破壞封裝
  • 下例演示瞭如何定義使用友元函數與友元類訪問private變量
  1 //
  2 //  main.cpp
  3 //  LeetCode
  4 //
  5 //  Created by Hao on 2017/3/16.
  6 //  Copyright © 2017年 Hao. All rights reserved.
  7 //
  8 
  9 #include <iostream>
 10 using namespace std;
 11 
 12 // 類的前置聲明
 13 class X;
 14 
 15 class Y
 16 {
 17 public:
 18     void f(X *);    // 對前置聲明的類只能使用指針做爲行參,由於在32位系統下,一個指針佔4個字節是固定的,編譯器可預判的
 19 //    void b(X);    // 用類做爲行參則出錯,由於編譯器沒法判斷類的類型及大小
 20     
 21 private:
 22     X*  pX;
 23 };
 24 
 25 /*
 26 // ERROR : Variable has incomplete type 'X'
 27 void Y::b(X x)
 28 {
 29 }
 30 */
 31 
 32 class X
 33 {
 34 public:
 35     void initialize();
 36     void print();
 37     friend void fG(X *, int);   // Global friend
 38     friend void Y::f(X *);      // class member friend
 39     friend class Z;             // Entire class is a friend
 40     friend void h();            // Global friend
 41     
 42 private:
 43     int i;
 44 };
 45 
 46 void X::initialize()
 47 {
 48     i = 0;
 49 }
 50 
 51 void X::print()
 52 {
 53     cout << "i = " << i << "\n" << endl;
 54 }
 55 
 56 void fG(X *x, int i)
 57 {
 58     x->i = i;
 59 }
 60 
 61 void Y::f(X *x)
 62 {
 63     x->i = 47;
 64 }
 65 
 66 class Z
 67 {
 68 public:
 69     void initialize();
 70     void g(X *x);
 71     
 72 private:
 73     int j;
 74 };
 75 
 76 void Z::initialize()
 77 {
 78     j = 100;
 79 }
 80 
 81 void Z::g(X *x)
 82 {
 83     x->i += j;
 84 }
 85 
 86 void h()
 87 {
 88     X x;
 89     
 90     x.i = 100;  // Direct data manipulation
 91     x.print();
 92 }
 93 
 94 int main ()
 95 {
 96     X x;
 97     
 98     x.initialize();
 99     x.print();
100     
101     // friend void fG(X *, int);   // Global friend
102     fG(&x, 9);
103     x.print();
104 
105     // friend void Y::f(X *);      // class member friend
106     Y y;
107     
108     y.f(&x);
109     x.print();
110 
111     // friend class Z;             // Entire class is a friend
112     Z z;
113     
114     z.initialize();
115     z.g(&x);
116     x.print();
117 
118     // friend void h();            // Global friend
119     h();
120     
121     return 0;
122 }
View Code
i = 0

i = 9

i = 47

i = 147

i = 100

Program ended with exit code: 0
View Result

Valgrind內存檢測工具

  • 使用工具檢查程序中存在的內存問題
  • 使用Valgrind完成相關功能
  • Valgrind Home
    • http://valgrind.org
  • valgrind_百度百科
    • https://baike.baidu.com/item/valgrind

運算符重載

  • 重載賦值運算符若是一個類提供了拷貝構造函數,那麼也要提供一個重載的賦值運算函數
    • 若是一個類沒有提供賦值運算函數,則默認提供一個。。。可是會存在淺拷貝的問題。
  • C++語言規定
    • 重載運算符要保持原有運算符的意義
    • 只能對已有的運算符重載,不能增長新的運算符
    • 重載的運算符不會改變原有的優先級和結合性
  • 運算符重載的方式如下四個運算符不能被重載::: / .* / . / ?
    • 成員函數
    • 友元函數
  • C++規定,參數說明都是內部類型時(e.g. int),不能重載。
  • 做爲成員的運算符比做爲友元的運算符,在聲明和定義時,形式上少一個參數。
  • C++規定:=,(),[],-> 這四種運算符必須爲成員形式
  • 下例演示了自增運算符的重載。能夠看到前增量效率更高,由於它不須要拷貝構造來保存原有對象值。還須要注意後增量運算符中的參數int只爲了區別前增量與後增量,除此以外沒有任何做用。
 1 //
 2 //  Increase.hpp
 3 //  LeetCode
 4 //
 5 //  Created by Hao on 2017/12/28.
 6 //  Copyright © 2017年 Hao. All rights reserved.
 7 //
 8 
 9 #ifndef Increase_hpp
10 #define Increase_hpp
11 
12 class Increase
13 {
14 public:
15     Increase(int val);
16     ~Increase();
17     
18     Increase& operator++ ();        // prefix, return reference
19     Increase operator++ (int val);  // postfix, return value
20     
21     int getVal() const
22     {
23         return m_val;
24     }
25     
26 private:
27     int m_val;
28 };
29 
30 #endif /* Increase_hpp */
Increase.hpp
 1 //
 2 //  Increase.cpp
 3 //  LeetCode
 4 //
 5 //  Created by Hao on 2017/12/28.
 6 //  Copyright © 2017年 Hao. All rights reserved.
 7 //
 8 
 9 #include "Increase.hpp"
10 
11 Increase::Increase(int val)
12     : m_val(val)
13 {
14 }
15 
16 Increase::~Increase()
17 {
18 }
19 
20 Increase& Increase::operator++ ()
21 {
22     ++ m_val;
23     return *this;
24 }
25 
26 Increase Increase::operator++ (int)
27 {
28     Increase ret(m_val);
29     ++ m_val;
30     
31     return ret;
32 }
Increase.cpp
 1 //
 2 //  main.cpp
 3 //  LeetCode
 4 //
 5 //  Created by Hao on 2017/3/16.
 6 //  Copyright © 2017年 Hao. All rights reserved.
 7 //
 8 
 9 #include "Increase.hpp"
10 
11 #include <iostream>
12 using namespace std;
13 
14 int main ()
15 {
16     Increase    val(100);
17     Increase    val2 = ++ val;
18     
19     cout << "val = " << val.getVal() << "\n" << endl;
20     cout << "val2 = " << val2.getVal() << "\n" << endl;
21     
22     Increase    val3 = val ++;
23     
24     cout << "val3 = " << val3.getVal() << "\n" << endl;
25     cout << "val = " << val.getVal() << "\n" << endl;
26     
27     return 0;
28 }
main.cpp
val = 101

val2 = 101

val3 = 101

val = 102

Program ended with exit code: 0
View Result

String類的運算符重載

  • 下例演示瞭如何編寫String類的構造函數/析構函數/拷貝構造函數/運算符重載 
 1 //
 2 //  TString.hpp
 3 //  LeetCode
 4 //
 5 //  Created by Hao on 2017/12/28.
 6 //  Copyright © 2017年 Hao. All rights reserved.
 7 //
 8 
 9 #ifndef TString_hpp
10 #define TString_hpp
11 
12 #include <iostream>
13 using namespace std;
14 
15 namespace T {
16 
17     class String
18     {
19     public:
20         String (const char * = nullptr);
21         ~String ();
22         
23         String (const String &);
24 
25         // 重載賦值運算符
26 
27         // String a; a = b;
28         String& operator= (const String &);
29         // String a; a = "hello";
30         String& operator= (const char *);
31         
32         String& operator+= (const String &);
33         String operator+ (const String &) const;
34         
35         String& operator+= (const char *);
36         String operator+ (const char *) const;
37         
38         inline const char * data() const
39         {
40             return m_data;
41         }
42         
43     private:
44         char *m_data;
45     };
46 
47 }
48 
49 
50 #endif /* TString_hpp */
TString.hpp
  1 //
  2 //  TString.cpp
  3 //  LeetCode
  4 //
  5 //  Created by Hao on 2017/12/28.
  6 //  Copyright © 2017年 Hao. All rights reserved.
  7 //
  8 
  9 #include "TString.hpp"
 10 
 11 #include <iostream>
 12 #include <cstring>
 13 using namespace std;
 14 
 15 namespace T
 16 {
 17 
 18     String::String(const char *str)
 19     {
 20         if (nullptr == str) {
 21             m_data = new char[1];
 22             *m_data = '\0';
 23         } else {
 24             int length = strlen(str);
 25             m_data = new char[length + 1];
 26             strcpy(m_data, str);
 27         }
 28     }
 29 
 30     String::~String()
 31     {
 32         delete [] m_data;
 33     }
 34 
 35     String::String(const String & other)
 36     {
 37         int length = strlen(other.m_data);
 38         
 39         m_data = new char[length + 1];
 40         strcpy(m_data, other.m_data);
 41     }
 42 
 43     String& String::operator= (const String &other)
 44     {
 45         // 檢查自賦值
 46         if (this == &other) {
 47             return *this;
 48         }
 49         
 50         // 釋放原有內存資源
 51         delete[] m_data;
 52         
 53         int length = strlen(other.m_data);
 54         m_data = new char[length + 1];
 55         strcpy(m_data, other.m_data);
 56         
 57         return *this;
 58     }
 59 
 60     String& String::operator= (const char *other)
 61     {
 62         delete[] m_data;
 63         
 64         if (nullptr == other) {
 65             m_data = new char[1];
 66             *m_data = '\0';
 67         } else {
 68             int length = strlen(other);
 69             m_data = new char[length + 1];
 70             strcpy(m_data, other);
 71         }
 72         
 73         return *this;
 74     }
 75     
 76     String& String::operator+= (const T::String &other)
 77     {
 78         char *tmp = m_data;
 79         int length = strlen(m_data) + strlen(other.m_data);
 80         
 81         m_data = new char[length + 1];
 82         strcpy(m_data, tmp);
 83         strcat(m_data, other.m_data);
 84         
 85         delete [] tmp;
 86         
 87         return *this;
 88     }
 89     
 90     String String::operator+ (const T::String &other) const
 91     {
 92         String result;
 93         
 94         result += *this;
 95         result += other;
 96 
 97         return result;
 98     }
 99     
100     String& String::operator+= (const char *other)
101     {
102         String tmp(other);
103         
104         *this += tmp;
105         
106         return *this;
107     }
108     
109     String String::operator+ (const char *other) const
110     {
111         String result = *this;
112         
113         result += other;
114         
115         return result;
116     }
117 
118 } // end of namespace T
TString.cpp
 1 //
 2 //  main.cpp
 3 //  LeetCode
 4 //
 5 //  Created by Hao on 2017/3/16.
 6 //  Copyright © 2017年 Hao. All rights reserved.
 7 //
 8 
 9 #include "TString.hpp"
10 
11 #include <iostream>
12 using namespace std;
13 
14 using namespace T;
15 
16 int main ()
17 {
18     String s1("hello ");
19     
20     String s2 = s1;
21     
22     String s3 = "world ";
23     
24     cout << "s1 = " << s1.data() << "\n" << endl;
25 
26     cout << "s2 = " << s2.data() << "\n" << endl;
27 
28     cout << "s3 = " << s3.data() << "\n" << endl;
29 
30     //    String& operator= (const char *);
31     s1 = "hello world ";
32     
33     cout << "s1 = " << s1.data() << "\n" << endl;
34 
35     //     String& operator= (const String &);
36     s3 = s1;
37     
38     cout << "s3 = " << s3.data() << "\n" << endl;
39     
40     //    String& operator+= (const String &);
41     s1 += s3;
42     
43     cout << "s1 = " << s1.data() << "\n" << endl;
44     
45     //    String& operator+= (const char *);
46     s3 += "!";
47     
48     cout << "s3 = " << s3.data() << "\n" << endl;
49     
50     //    String operator+ (const String &) const;
51     String s4 = s1 + s2;
52     
53     cout << "s4 = " << s4.data() << "\n" << endl;
54     
55     //    String operator+ (const char *) const;
56     s4 = s1 + "hello ";
57     
58     cout << "s4 = " << s4.data() << "\n" << endl;
59 
60     return 0;
61 }
main.cpp
s1 = hello 

s2 = hello 

s3 = world 

s1 = hello world 

s3 = hello world 

s1 = hello world hello world 

s3 = hello world !

s4 = hello world hello world hello 

s4 = hello world hello world hello 

Program ended with exit code: 0
View Result

繼承 

  • C++中繼承有三種方式:公有繼承,私有繼承,多重繼承(保護繼承比較少見)
  • 統一建模語言_百度百科
    • https://baike.baidu.com/item/統一建模語言/3160571?fromtitle=UML&fromid=446747
  • Download Astah Products | Astah.net在構造一個子類時,完成其父類部分的構造由父類的構造函數完成。
    • http://astah.net/download
  • 子類與父類的構造析構順序:先構造父類,後構造子類;先析構子類,後析構父類。
  • 下例演示了公有繼承,以及如何構造子類
 1 //
 2 //  Animal.hpp
 3 //  LeetCode
 4 //
 5 //  Created by Hao on 2017/12/29.
 6 //  Copyright © 2017年 Hao. All rights reserved.
 7 //
 8 
 9 #ifndef Animal_hpp
10 #define Animal_hpp
11 
12 #include <string>
13 using namespace std;
14 
15 namespace T
16 {
17     class Animal
18     {
19     public:
20         Animal(int age, string location);
21         ~Animal();
22         
23         void setAge(int age);
24         int getAge() const;
25         
26         string getLocation() const;
27         
28     protected:
29         void setLocation(string location);
30         
31         string m_location;
32         
33     private:
34         int m_age;
35     };
36     
37     class Cat : public Animal
38     {
39     public:
40         Cat(int age, int color, string location);
41         ~Cat();
42         
43         int getColor() const;
44         void setColor(int color);
45         
46         void setCatLocation(string location);
47         
48     private:
49         int m_color;
50     };
51     
52     class Dog : public Animal
53     {
54     public:
55         Dog(int age, int weight, string location);
56         ~Dog();
57         
58         int getWeight() const;
59         void setWeight(int weight);
60         
61     private:
62         int m_weight;
63     };
64 }
65 
66 #endif /* Animal_hpp */
Animal.hpp
 1 //
 2 //  Animal.cpp
 3 //  LeetCode
 4 //
 5 //  Created by Hao on 2017/12/29.
 6 //  Copyright © 2017年 Hao. All rights reserved.
 7 //
 8 
 9 #include "Animal.hpp"
10 
11 #include <iostream>
12 using namespace std;
13 
14 namespace T
15 {
16     Animal::Animal(int age, string location)
17         : m_age(age), m_location(location)
18     {
19         cout << "Animal constructing --->\n" << endl;
20     }
21     
22     Animal::~Animal()
23     {
24         cout << "Animal destructing --->\n" << endl;
25     }
26     
27     int Animal::getAge() const
28     {
29         return m_age;
30     }
31     
32     void Animal::setAge(int age)
33     {
34         m_age = age;
35     }
36     
37     string Animal::getLocation() const
38     {
39         return m_location;
40     }
41     
42     void Animal::setLocation(string location)
43     {
44         cout << __func__ << " --->\n" << endl;
45     }
46     
47     Cat::Cat(int age, int color, string location)
48         : Animal(age, location), m_color(color)
49     {
50         cout << "Cat constructing --->\n" << endl;
51     }
52     
53     Cat::~Cat()
54     {
55         cout << "Cat destructing --->\n" << endl;
56     }
57     
58     int Cat::getColor() const
59     {
60         return m_color;
61     }
62     
63     void Cat::setColor(int color)
64     {
65         m_color = color;
66     }
67     
68     void Cat::setCatLocation(string location)
69     {
70         Animal::setLocation(location);
71     }
72     
73     Dog::Dog(int age, int weight, string location)
74         : Animal(age, location), m_weight(weight)
75     {
76         cout << "Dog constructing --->\n" << endl;
77     }
78     
79     Dog::~Dog()
80     {
81         cout << "Dog destructing --->\n" << endl;
82     }
83     
84     int Dog::getWeight() const
85     {
86         return m_weight;
87     }
88     
89     void Dog::setWeight(int weight)
90     {
91         m_weight = weight;
92     }
93     
94 } // end of namespace T
Animal.cpp
 1 //
 2 //  main.cpp
 3 //  LeetCode
 4 //
 5 //  Created by Hao on 2017/3/16.
 6 //  Copyright © 2017年 Hao. All rights reserved.
 7 //
 8 
 9 #include "Animal.hpp"
10 
11 using namespace T;
12 
13 int main ()
14 {
15     {
16         Cat cat(1, 1, "house");
17         cat.setAge(2);
18         cat.setColor(2);
19         
20         cat.setCatLocation("apartment");
21     }
22     
23     {
24         Dog dog(2, 2, "home");
25         dog.setAge(3);
26         dog.setWeight(3);
27     }
28 
29     return 0;
30 }
main.cpp
Animal constructing --->

Cat constructing --->

setLocation --->

Cat destructing --->

Animal destructing --->

Animal constructing --->

Dog constructing --->

Dog destructing --->

Animal destructing --->

Program ended with exit code: 0
View Result
  •  不一樣繼承方式的影響主要體如今
    • 派生類成員對基類成員的訪問權限
    • 經過派生類對象對基類成員的訪問權限
  • 里氏代換原則_百度百科
    • https://baike.baidu.com/item/里氏代換原則
    • 里氏代換原則(Liskov Substitution Principle LSP)面向對象設計的基本原則之一。 里氏代換原則中說,任何基類能夠出現的地方,子類必定能夠出現。 LSP是繼承複用的基石,只有當衍生類能夠替換掉基類,軟件單位的功能不受到影響時,基類才能真正被複用,而衍生類也可以在基類的基礎上增長新的行爲。里氏代換原則是對「開-閉」原則的補充。實現「開-閉」原則的關鍵步驟就是抽象化。而基類與子類的繼承關係就是抽象化的具體實現,因此里氏代換原則是對實現抽象化的具體步驟的規範。
    • 在進行設計的時候,儘可能從抽象類繼承,而不是從具體類繼承。若是從繼承等級樹來看,全部葉子節點應當是具體類,而全部的樹枝節點應當是抽象類或者接口。固然這個只是一個通常性的指導原則,使用的時候還要具體狀況具體分析。
    • 簡單的理解爲一個軟件實體若是使用的是一個父類,那麼必定適用於其子類,並且它察覺不出父類對象和子類對象的區別。也就是說,軟件裏面,把父類都替換成它的子類,程序的行爲沒有變化。
  • 開閉原則_百度百科
    • https://baike.baidu.com/item/開閉原則
    • 開閉原則(OCP)是面向對象設計中「可複用設計」的基石,是面向對象設計中最重要的原則之一,其它不少的設計原則都是實現開閉原則的一種手段。對於擴展是開放的,對於修改是關閉的,這意味着模塊的行爲是能夠擴展的。當應用的需求改變時,咱們能夠對模塊進行擴展,使其具備知足那些改變的新行爲。也就是說,咱們能夠改變模塊的功能。對模塊行爲進行擴展時,沒必要改動模塊的源代碼或者二進制代碼。模塊的二進制可執行版本,不管是可連接的庫、DLL或者.EXE文件,都無需改動。
  • 公有繼承(public)
    • 基類的public和protected成員的訪問屬性在派生類中保持不變,但基類的private成員不可直接訪問。
    • 派生類中的成員函數能夠直接訪問基類中的public和protected成員,但不能直接訪問基類的private成員。
    • 經過派生類對象只能訪問基類的public成員。
  • 私有繼承(private)
    • 基類的public和protected成員都以private身份出如今派生類中,但基類的private成員不可直接訪問。
    • 派生類中的成員函數能夠直接訪問基類中的public和protected成員,但不能直接訪問基類的private成員。
    • 經過派生類的對象不能直接訪問基類中的任何成員。
  • 多重繼承
    • 能夠爲一個派生類指定多個基類,這樣的繼承結構稱爲多重繼承或多繼承
    • Java/C#中沒有多繼承,C++中也應避免使用
    • 當兩個父類有一樣的成員時會帶來模糊性,這樣致使了名稱衝突(name collision),在編譯時將予以拒絕,也稱之爲菱形繼承
    • 能夠在方法前說明基類,或者用虛繼承來解決菱形繼承問題
  • 虛繼承 - 維基百科,自由的百科全書
    • https://zh.wikipedia.org/wiki/虛繼承
    • 虛繼承 是面向對象編程中的一種技術,是指一個指定的基類,在繼承體系結構中,將其成員數據實例共享給也從這個基類型直接或間接派生的其它類。
    • 舉例來講:假如類A和類B各自從類X派生(非虛繼承且假設類X包含一些數據成員),且類C同時多繼承自類A和B,那麼C的對象就會擁有兩套X的實例數據(可分別獨立訪問,通常要用適當的消歧義限定符)。可是若是類A與B各自虛繼承了類X,那麼C的對象就只包含一套類X的實例數據。對於這一律念典型實現的編程語言是C++。
    • 這一特性在多重繼承應用中很是有用,可使得虛基類對於由它直接或間接派生的類來講,擁有一個共同的基類對象實例。避免因爲帶有歧義的組合而產生的問題(如「菱形繼承問題」)。其原理是,間接派生類(C)穿透了其父類(上面例子中的A與B),實質上直接繼承了虛基類X。
    • 這一律念通常用於「繼承」在表現爲一個總體,而非幾個部分的組合時。在C++中,基類能夠經過使用關鍵字virtual來聲明虛繼承關係。
  • 構造對象的規則須要擴展以控制多重繼承。構造函數按下列順序被調用:
    • 任何虛擬基類的構造函數按照它們被繼承的順序調用。
    • 任何非虛擬基類的構造函數按照它們被繼承的順序調用。
    • 任何成員對象的構造函數按照它們聲明的順序調用。
    • 再調用類本身的構造函數
  • 下例演示了虛繼承以及多重繼承的使用。注意使用了虛繼承以後類的size大小的不一樣。
    • 虛函數、虛繼承對sizeof的影響 - CSDN博客
      • http://blog.csdn.net/acb0y/article/details/8822983
 1 //
 2 //  Furniture.hpp
 3 //  LeetCode
 4 //
 5 //  Created by Hao on 2017/12/31.
 6 //  Copyright © 2017年 Hao. All rights reserved.
 7 //
 8 
 9 #ifndef Furniture_hpp
10 #define Furniture_hpp
11 
12 #include <iostream>
13 using namespace std;
14 
15 class Furniture
16 {
17 public:
18     Furniture(int weight = 0)
19         : m_weight(weight)
20     {
21         cout << "Furniture constructing --->\n" << endl;
22     }
23     
24     void setWeight(int weight)
25     {
26         m_weight = weight;
27     }
28     
29     int getWeight() const
30     {
31         return m_weight;
32     }
33     
34 private:
35     int m_weight;
36 };
37 
38 // Two classes virtually inheriting Furniture
39 class Sofa : public virtual Furniture
40 {
41 public:
42     Sofa(int weight = 0);
43     
44     void watchTV()
45     {
46         cout << __func__ << "\n" << endl;
47     }
48 };
49 
50 class Bed : public virtual Furniture
51 {
52 public:
53     Bed(int weight = 0);
54     
55     void sleep()
56     {
57         cout << __func__ << "\n" << endl;
58     }
59 };
60 
61 // 多重繼承
62 class SofaBed : public Sofa, public Bed
63 {
64 public:
65     SofaBed();
66     
67     void foldout()
68     {
69         cout << __func__ << "\n" << endl;
70     }
71 };
72 
73 #endif /* Furniture_hpp */
Furniture.hpp
 1 //
 2 //  Furniture.cpp
 3 //  LeetCode
 4 //
 5 //  Created by Hao on 2017/12/31.
 6 //  Copyright © 2017年 Hao. All rights reserved.
 7 //
 8 
 9 #include "Furniture.hpp"
10 
11 // Should not set weight = 0, or else ERROR Redefinition of default argument
12 Sofa::Sofa(int weight)
13     : Furniture(weight)
14 {
15     cout << "Sofa constructing --->\n" << endl;
16 }
17 
18 Bed::Bed(int weight)
19     : Furniture(weight)
20 {
21     cout << "Bed constructing --->\n" << endl;
22 }
23 
24 SofaBed::SofaBed()
25 {
26     cout << "SofaBed constructing --->\n" << endl;
27 }
Furniture.cpp
 1 //
 2 //  main.cpp
 3 //  LeetCode
 4 //
 5 //  Created by Hao on 2017/3/16.
 6 //  Copyright © 2017年 Hao. All rights reserved.
 7 //
 8 
 9 #include "Furniture.hpp"
10 
11 int main ()
12 {
13     Furniture   furniture;
14     Sofa    sofa;
15     Bed     bed;
16     SofaBed sofabed;
17     
18     cout << "sizeof(Furniture) : " << sizeof(Furniture) << "\n" << endl;
19     cout << "sizeof(Sofa) : " << sizeof(Sofa) << "\n" << endl;
20     cout << "sizeof(Bed) : " << sizeof(Bed) << "\n" << endl;
21     cout << "sizeof(SofaBed) : " << sizeof(SofaBed) << "\n" << endl;
22     
23     sofabed.watchTV();
24     
25     sofabed.sleep();
26     
27     sofabed.foldout();
28 
29     return 0;
30 }
main.cpp
Furniture constructing --->

Furniture constructing --->

Sofa constructing --->

Furniture constructing --->

Bed constructing --->

Furniture constructing --->

Sofa constructing --->

Bed constructing --->

SofaBed constructing --->

sizeof(Furniture) : 4

sizeof(Sofa) : 16

sizeof(Bed) : 16

sizeof(SofaBed) : 24

watchTV

sleep

foldout

Program ended with exit code: 0
View Result
  •  下例演示了繼承與組合,優先使用組合而不是繼承。
  1 //
  2 //  Car.hpp
  3 //  LeetCode
  4 //
  5 //  Created by Hao on 2018/1/3.
  6 //  Copyright © 2018年 Hao. All rights reserved.
  7 //
  8 
  9 #ifndef Car_hpp
 10 #define Car_hpp
 11 
 12 #include <iostream>
 13 using namespace std;
 14 
 15 namespace T {
 16     
 17     class Engine {
 18     public:
 19         Engine(int id)
 20             : m_id(id)
 21         {
 22             cout << "Engine constructing\n" << endl;
 23         }
 24         
 25         ~Engine()
 26         {
 27             cout << "Engine destructing\n" << endl;
 28         }
 29         
 30         void start()
 31         {
 32             cout << "Engine start\n" << endl;
 33         }
 34         
 35         void stop()
 36         {
 37             cout << "Engine stop\n" << endl;
 38         }
 39         
 40     private:
 41         int m_id;
 42     };
 43     
 44     class Wheel {
 45     public:
 46         Wheel(int id)
 47             : m_id(id)
 48         {
 49             cout << "Wheel constructing\n" << endl;
 50         }
 51         
 52         ~Wheel()
 53         {
 54             cout << "Wheel destructing\n" << endl;
 55         }
 56         
 57         void roll()
 58         {
 59             cout << "Wheel rolling\n" << endl;
 60         }
 61         
 62     private:
 63         int m_id;
 64     };
 65     
 66     // class Car : public Engine, public Wheel  // 避免使用多繼承
 67     class Car {
 68     public:
 69         Car(Engine *, Wheel *, string, int);
 70         
 71         ~Car()
 72         {
 73             cout << "Car destructing\n" << endl;
 74         };
 75         
 76         void run();
 77         void stop();
 78         
 79     private:
 80         Car(const Car&);
 81         Car& operator= (const Car&);
 82         
 83         Engine  *m_engine;
 84         Wheel   *m_wheel;
 85         string  m_name;
 86         int     m_price;
 87     };
 88     
 89     class Stero {
 90     public:
 91         Stero()
 92         {
 93             cout << "Stero constructing\n" << endl;
 94         }
 95         
 96         ~Stero()
 97         {
 98             cout << "Stero destructing\n" << endl;
 99         }
100         
101         void play()
102         {
103             cout << "Stero playing\n" << endl;
104         }
105     };
106     
107     class Benchi : public Car {
108     public:
109         Benchi(Engine *, Wheel *, string, int, Stero *);
110         
111         ~Benchi()
112         {
113             cout << "Benchi destructing\n" << endl;
114         }
115         
116         void musicOn();
117         
118     private:
119         Stero *m_stero;
120     };
121     
122     class Transformer : public Car {
123     public:
124         Transformer(Engine *, Wheel *, string, int, bool);
125         
126         ~Transformer()
127         {
128             cout << "Transformer destructing\n" << endl;
129         }
130         
131         void fight();
132         void transform();
133         
134     private:
135         bool    m_val;
136     };
137     
138 }
139 
140 #endif /* Car_hpp */
Car.hpp
 1 //
 2 //  Car.cpp
 3 //  LeetCode
 4 //
 5 //  Created by Hao on 2018/1/3.
 6 //  Copyright © 2018年 Hao. All rights reserved.
 7 //
 8 
 9 #include "Car.hpp"
10 
11 namespace T {
12     Car::Car(Engine *e, Wheel *w, string name, int price)
13     : m_engine(e), m_wheel(w), m_name(name), m_price(price)
14     {
15         cout << "Car constructing\n" << endl;
16     }
17     
18     void Car::run()
19     {
20         m_engine->start();
21         m_wheel->roll();
22         
23         cout << "Car running\n" << endl;
24     }
25     
26     void Car::stop()
27     {
28         m_engine->stop();
29         
30         cout << "Car stopping\n" << endl;
31     }
32     
33     Benchi::Benchi(Engine *e, Wheel *w, string name, int price, Stero *st)
34     : Car(e, w, name, price), m_stero(st)
35     {
36         cout << "Benchi constructing\n" << endl;
37     }
38     
39     void Benchi::musicOn()
40     {
41         Car::run();
42         m_stero->play();
43         
44         cout << "Music on\n" << endl;
45     }
46     
47     Transformer::Transformer(Engine *e, Wheel *w, string name, int price, bool val)
48     :Car(e, w, name, price), m_val(val)
49     {
50         cout << "Transformer constructing\n" << endl;
51     }
52     
53     void Transformer::fight()
54     {
55         run();
56         
57         cout << "Transformer fight\n" << endl;
58     }
59     
60     void Transformer::transform()
61     {
62         run();
63         
64         cout << "Transformer transform\n" << endl;
65     }
66 }
Car.cpp
 1 //
 2 //  main.cpp
 3 //  LeetCode
 4 //
 5 //  Created by Hao on 2017/3/16.
 6 //  Copyright © 2017年 Hao. All rights reserved.
 7 //
 8 
 9 #include "Car.hpp"
10 using namespace T;
11 
12 int main ()
13 {
14     Engine  e1(1);
15     Wheel   w1(1);
16     Stero   stero;
17     Benchi  benchi(&e1, &w1, "benchi", 100, &stero);
18 
19     benchi.musicOn();
20     
21     Transformer t(&e1, &w1, "optimusprime", 200, true);
22     
23     t.transform();
24     t.fight();
25     
26     return 0;
27 }
main.cpp
Engine constructing

Wheel constructing

Stero constructing

Car constructing

Benchi constructing

Engine start

Wheel rolling

Car running

Stero playing

Music on

Car constructing

Transformer constructing

Engine start

Wheel rolling

Car running

Transformer transform

Engine start

Wheel rolling

Car running

Transformer fight

Transformer destructing

Car destructing

Benchi destructing

Car destructing

Stero destructing

Wheel destructing

Engine destructing

Program ended with exit code: 0
View Result

多態 

  • 多態性是指「多種行爲」
  • 一樣的方法調用而執行不一樣操做、運行不一樣代碼
  • 多態經過分離作什麼和怎麼作,從另外一個角度將接口和實現進行分離
    • 「封裝」經過合併特徵和行爲來建立新的數據類型
    • 「實現隱藏」經過將細節「私有化」把接口和實現進行分離
  • 「多態」則消除了類型之間的耦合關係
  • LSP(Liskov替換原則):子類型必須可以替換掉它的基類型
  • 多態的概念基於對象引用的動態綁定特性,動態綁定是多態現象的根源
  • 多態實現過程
    • 子類重寫父類的方法
    • 代碼中向父類型變量發出消息(靜態綁定)
    • 運行時,根據變量實際引用的對象類型決定調用哪一個方法(動態綁定)
  • 靜態綁定在編譯器進行
  • 動態綁定在運行期進行
  • 虛函數與抽象類
  • 虛函數 - 維基百科,自由的百科全書
    • https://zh.wikipedia.org/wiki/虛函數_(程序語言)
    • 虛函數是面向對象程序設計中的一個重要的概念。只能適用於指針和參考的計算機工程運算。當從父類中繼承的時候,虛函數和被繼承的函數具備相同的簽名。可是在運行過程當中,運行系統將根據對象的類型,自動地選擇適當的具體實現運行。虛函數是面向對象編程實現多態的基本手段。
    • 虛函數在設計模式方面扮演重要角色。例如,《設計模式》一書中提到的23種設計模式中,僅5個對象建立模式就有4個用到了虛函數(抽象工廠、工廠方法、生成器、原型),只有單例沒有用到。
    • 在Java語言中, 全部的方法默認都是"虛函數". 只有以關鍵字 final 標記的方法纔是非虛函數. 
    • 在 C# 語言中, 對基類中的任何虛方法必須用 virtual 修飾, 而派生類中由基類繼承而來的重載方法必須用 override 修飾. 
    • 純虛函數或純虛方法是一個須要被非抽象的派生類覆蓋(override)的虛函數. 包含純虛方法的類被稱做抽象類; 抽象類不能被直接實例化。 一個抽象基類的一個子類只有在全部的純虛函數在該類(或其父類)內給出實現時, 才能直接實例化. 純虛方法一般只有聲明(簽名)而沒有定義(實現),但有特例情形要求純虛函數必須給出函數體定義.
    • 做爲一個例子, 抽象基類"MathSymbol"可能提供一個純虛函數 doOperation(), 和派生類 "Plus" 和 "Minus" 提供doOperation() 的具體實現. 因爲 "MathSymbol" 是一個抽象概念, 爲其每一個子類定義了同一的動做, 在 "MathSymbol" 類中執行 doOperation() 沒有任何意義. 相似的, 一個給定的 "MathSymbol" 子類若是沒有 doOperation() 的具體實現是不徹底的.
    • 雖然純虛方法一般在定義它的類中沒有實現, 在 C++ 語言中, 容許純虛函數在定義它的類中包含其實現, 這爲派生類提供了備用或默認的行爲. C++的虛基類的虛析構函數必須提供函數體定義,不然連接時(linking)在析構該抽象類的派生實例對象的語句處會報錯。
    • 在C++語言中, 純虛函數用一種特別的語法[=0]定義(但 VS 也支持 abstract 關鍵字:virtual ReturnType Function()abstract;)
    • 純虛函數的定義僅提供方法的原型. 雖然在抽象類中一般不提供純虛函數的實現, 可是抽象類中能夠包含其實現, 並且能夠不在聲明的同時給出定義[2]. 每一個非抽象子類仍然須要重載該方法
  • 抽象類_百度百科
    • https://baike.baidu.com/item/抽象類
    • 抽象類每每用來表徵對問題領域進行分析、設計中得出的抽象概念,是對一系列看上去不一樣,可是本質上相同的具體概念的抽象。一般在編程語句中用 abstract 修飾的類是抽象類。在C++中,含有純虛擬函數的類稱爲抽象類,它不能生成對象;在java中,含有抽象方法的類稱爲抽象類,一樣不能生成對象。抽象類是不完整的,它只能用做基類。在 面向對象方法中,抽象類主要用來進行類型隱藏和充當全局變量的角色。
    • 與具體類比較
      • 抽象類不能直接實例化,而且對抽象類使用new運算符會致使編譯時錯誤。雖然一些變量和值在編譯時的類型能夠是抽象的,可是這樣的變量和值必須或者爲null,或者含有對非抽象類的實例的引用(此非抽象類是從抽象類派生的)
      • 容許(但不要求)抽象類包含抽象成員。
      • 抽象類不能被密封。
    • 與接口比較
      • 抽象類表示該類中可能已經有一些方法的具體定義,可是接口就僅僅只能定義各個方法的界面(方法名,參數列表,返回類型),並不關心具體細節。
      • 接口是引用類型的,和抽象類的類似之處有三點:
        • 不能實例化;
        • 包含未實現的方法聲明;
        • 派生類必須實現未實現的方法,抽象類是抽象方法,接口則是全部成員(不只是方法包括其餘成員)。
      • 抽象類與接口緊密相關。然而接口又比抽象類更抽象,這主要體如今它們的差異上:
        • 類能夠實現無限個接口,但僅能從一個抽象(或任何其餘類型)類繼承,從抽象類派生的類仍可實現接口,從而得出接口是用來解決多重繼承問題的。
        • 抽象類當中能夠存在非抽象的方法,可接口不能,且它裏面的方法只是一個聲明必須用public來修飾沒有具體實現的方法。
        • 抽象類中的成員變量能夠被不一樣的修飾符來修飾,可接口中的成員變量默認的都是靜態常量(static final)。
        • 抽象類是對象的抽象,然而接口是一種行爲規範。
    • 標準c++沒有abstract關鍵字,代之使用純虛類實現相似的功能,詳見詞條「虛類」。在實現接口時,常寫一個抽象類,來實現接口中的某些子類所需的通用方法,接着在編寫各個子類時,便可繼承該抽象類來使用,省去在每一個都要實現通用的方法的困擾。
    • 爲了讓一個類成爲抽象類,至少必須有一個純虛函數。包含至少一個純虛函數的類視爲抽象類。將函數初始化爲0使它們成爲純虛函數,沒有0這個初使化器,它們僅僅是虛函數。
    • 抽象類對於提供模式、藍圖和後代類遵循的原則有用,若是遵循了藍圖的語義,後代類的行爲可能按抽象類提供者和使用者所指望的那樣。
    • 經過使用抽象類,C++程序員能夠提供C++組件的規範,在它的構建中指導組件的實現者。
    • 在面向對象方法中,抽象類主要用來進行類型隱藏。構造出一個固定的一組行爲的抽象描述,可是這組行爲卻可以有任意個可能的具體實現方式。這個抽象描述就是抽象類,而這一組任意個可能的具體實現則表現爲全部可能的派生類。模塊能夠操做一個抽象體。因爲模塊依賴於一個固定的抽象體,所以它能夠是不容許修改的;同時,經過從這個抽象體派生,也可擴展此模塊的行爲功能。爲了可以實現面向對象設計的一個最核心的原則OCP(Open-Closed Principle),抽象類是其中的關鍵所在。
  • 虛類_百度百科
    • https://baike.baidu.com/item/虛類
    • 含有虛函數的類是虛類,虛函數用關鍵字virtual聲明。
    • 虛函數代表只有在程序使用到該函數時,纔得到與調用對象對應的該函數的實現。
    • 含有純虛函數的類是純虛類,更多的是叫抽象類。純虛類能夠有成員變量。純虛類不能實例化。
    • 虛函數必須是基類的非靜態成員函數,其訪問權限能夠是protected或public
    • 虛函數的做用是實現動態聯編,也就是在程序的運行階段動態地選擇合適的成員函數。
    • 純虛函數沒有函數體。
    • c++虛類至關與java裏面的抽象類,與接口的不一樣之處以下:
      • 一、一個子類只能繼承一個抽象類(虛類),但能實現多個接口;
      • 二、一個抽象類能夠有構造方法,接口沒有構造方法;
      • 三、一個抽象類中的方法不必定是抽象方法,即其中的方法能夠有實現(有方法體),接口中的方法是抽象方法,不能有方法體,只有聲明;
      • 四、一個抽象類能夠是public、private、protected、default, 接口只有public和default;
      • 五、一個抽象類中的方法能夠是public、private、protected、default, 接口中的方法只能是public。
      • 相同之處:都不能實例化。
  • 純虛函數與接口類
  • 純虛函數_百度百科
    • https://baike.baidu.com/item/純虛函數
    • 虛函數是一種特殊的虛函數,在許多狀況下,在基類中不能對虛函數給出有意義的實現,而把它聲明爲純虛函數,它的實現留給該基類的派生類去作。這就是純虛函數的做用。
    • 純虛函數可讓類先具備一個操做名稱,而沒有操做內容,讓派生類在繼承時再去具體地給出定義。凡是含有純虛函數的類叫作抽象類。這種類不能聲明對象,只是做爲基類爲派生類服務。除非在派生類中徹底實現基類中全部的的純虛函數,不然,派生類也變成了抽象類,不能實例化對象。
    • 通常而言純虛函數的函數體是缺省的,可是也能夠給出純虛函數的函數體(此時純虛函數變爲虛函數),這一點常常被人們忽視,調用純虛函數的方法爲baseclass::virtual function.
    • 多態性:指相同對象收到不一樣消息或不一樣對象收到相同消息時產生不一樣的實現動做。C++支持兩種多態性:編譯時多態性,運行時多態性。
      • a.編譯時多態性:經過重載函數和運算符重載實現。
      • b運行時多態性:經過虛函數和繼承實現。
    • 虛函數:虛函數是在基類中被聲明爲virtual,並在派生類中從新定義的成員函數,可實現成員函數的動態重載
    • 抽象類:包含純虛函數的類稱爲抽象類。因爲抽象類包含了沒有定義的純虛函數,因此不能定義抽象類的對象。
  • 接口(軟件接口)_百度百科
    • https://baike.baidu.com/item/接口/15422203#viewPageContent
    • 接口(軟件類接口)是指對協定進行定義的引用類型。其餘類型實現接口,以保證它們支持某些操做。接口指定必須由類提供的成員或實現它的其餘接口。與類類似,接口能夠包含方法、屬性、索引器和事件做爲成員。
    • 面向對象的接口
      • 在C++中,一個類被容許繼承多個類。可是在Java之後的語言不被容許。這樣,若是想繼承多個類時便很是困難。因此開發方想出了新辦法:接口。一個接口內,容許包含變量、常量等一個類所包含的基本內容。可是,接口中的函數不容許設定代碼,也就意味着不能把程序入口放到接口裏。由上能夠理解到,接口是專門被繼承的。接口存在的意義也是被繼承。和C++裏的抽象類裏的純虛函數是相同的。不能被實例化。
  • 接口類不能實例化,不能生成對象實例
  • 必須爲多態基類聲明virtual析構函數,不然用基類指針定義的派生類對象,在析構時只會調用基類析構函數,而不會調用派生類析構函數,從而形成內存泄漏。
  • 針對接口編程,而不是針對實現編程。
  • 下例演示了多態以及虛函數,虛析構函數的使用
  1 //
  2 //  Animal.hpp
  3 //  LeetCode
  4 //
  5 //  Created by Hao on 2018/1/4.
  6 //  Copyright © 2018年 Hao. All rights reserved.
  7 //
  8 
  9 #ifndef Animal_hpp
 10 #define Animal_hpp
 11 
 12 #include <iostream>
 13 using namespace std;
 14 
 15 namespace T
 16 {
 17     class Animal
 18     {
 19     public:
 20         Animal()
 21         {
 22             cout << "Animal constructing\n" << endl;
 23             
 24         }
 25         
 26         // 爲基類聲明虛析構函數
 27         virtual ~Animal()
 28         {
 29             cout << "Animal destructing\n" << endl;
 30         }
 31         
 32         // Need to add "const" to avoid error Member function 'makeSound' not viable: 'this' argument has type 'const T::Animal', but function is not marked const
 33         void makeSound() const
 34         {
 35             cout << "Animal make sound\n" << endl;
 36         }
 37         
 38         // "virtual" 只須要在聲明時加上,不須要在定義時加上
 39         virtual void smile() const;
 40 
 41         /*
 42         virtual void smile() const = 0; // pure virtual function
 43         */
 44         
 45         /*
 46         virtual void smile() const
 47         {
 48             cout << "Animal smile\n" << endl;
 49         }
 50         */
 51     };
 52     
 53     class Dog : public Animal
 54     {
 55     public:
 56         Dog()
 57         {
 58             cout << "Dog constructing\n" << endl;
 59         }
 60         
 61         ~Dog()
 62         {
 63             cout << "Dog destructing\n" << endl;
 64         }
 65         
 66         void makeSound() const
 67         {
 68             cout << "Dog make sound\n" << endl;
 69         }
 70 
 71         void smile() const
 72         {
 73             cout << "Dog smile\n" << endl;
 74         }
 75     };
 76 
 77     class Cat : public Animal
 78     {
 79     public:
 80         Cat()
 81         {
 82             cout << "Cat constructing\n" << endl;
 83         }
 84         
 85         ~Cat()
 86         {
 87             cout << "Cat destructing\n" << endl;
 88         }
 89         
 90         void makeSound() const
 91         {
 92             cout << "Cat make sound\n" << endl;
 93         }
 94         
 95         void smile() const
 96         {
 97             cout << "Cat smile\n" << endl;
 98         }
 99     };
100 }
101 
102 #endif /* Animal_hpp */
Animal.hpp
 1 //
 2 //  Animal.cpp
 3 //  LeetCode
 4 //
 5 //  Created by Hao on 2018/1/4.
 6 //  Copyright © 2018年 Hao. All rights reserved.
 7 //
 8 
 9 #include "Animal.hpp"
10 
11 // "virtual" 只須要在聲明時加上,不須要在定義時加上
12 void T::Animal::smile() const
13 {
14     cout << "Animal smile\n" << endl;
15 }
Animal.cpp
 1 //
 2 //  main.cpp
 3 //  LeetCode
 4 //
 5 //  Created by Hao on 2017/3/16.
 6 //  Copyright © 2017年 Hao. All rights reserved.
 7 //
 8 
 9 #include "Animal.hpp"
10 using namespace T;
11 
12 void func(const Animal & animal)
13 {
14     cout << "Pass by reference ---> \n" << endl;
15     
16     animal.makeSound();
17     animal.smile();
18 }
19 
20 void foo(Animal * pAnimal)
21 {
22     cout << "Pass by pointer ---> \n" << endl;
23 
24     pAnimal->makeSound();
25     pAnimal->smile();
26 }
27 
28 void bar(Animal animal)
29 {
30     cout << "Pass by value ---> \n" << endl;
31 
32     animal.makeSound();
33     animal.smile();
34 }
35 
36 int main ()
37 {
38     {
39         Dog dog;
40         Cat cat;
41         
42         cout << "sizeof(Animal) : " << sizeof(Animal) << "\n" << endl;
43         cout << "sizeof(Dog) : " << sizeof(Dog) << "\n" << endl;
44         cout << "sizeof(Cat) : " << sizeof(Cat) << "\n" << endl;
45 
46         func(dog);
47         
48         func(cat);
49         
50         foo(&dog);
51         
52         foo(&cat);
53         
54         bar(dog);
55         
56         bar(cat);
57     }
58     
59     cout << "爲多態基類聲明虛析構函數 --->\n" << endl;
60     
61     {
62         Animal * pCat = new Cat;
63         
64         pCat->makeSound();
65         pCat->smile();
66         
67         foo(pCat);
68         
69         // 若是不聲明爲虛析構函數,則只會調用基類Animal的析構函數,而不會調用派生類Cat的析構函數,形成內存泄漏
70         delete pCat;
71     }
72     
73     return 0;
74 }
main.cpp
Animal constructing

Dog constructing

Animal constructing

Cat constructing

sizeof(Animal) : 8

sizeof(Dog) : 8

sizeof(Cat) : 8

Pass by reference ---> 

Animal make sound

Dog smile

Pass by reference ---> 

Animal make sound

Cat smile

Pass by pointer ---> 

Animal make sound

Dog smile

Pass by pointer ---> 

Animal make sound

Cat smile

Pass by value ---> 

Animal make sound

Animal smile

Animal destructing

Pass by value ---> 

Animal make sound

Animal smile

Animal destructing

Cat destructing

Animal destructing

Dog destructing

Animal destructing

爲多態基類聲明虛析構函數 --->

Animal constructing

Cat constructing

Animal make sound

Cat smile

Pass by pointer ---> 

Animal make sound

Cat smile

// 若是不聲明爲虛析構函數,則只會調用基類Animal的析構函數,而不會調用派生類Cat的析構函數,形成內存泄漏
Cat destructing

Animal destructing

Program ended with exit code: 0
View Result
  •  下例筆試題演示了虛函數,虛繼承的使用
 1 //
 2 //  main.cpp
 3 //  LeetCode
 4 //
 5 //  Created by Hao on 2017/3/16.
 6 //  Copyright © 2017年 Hao. All rights reserved.
 7 //
 8 
 9 #include "iostream"
10 using namespace std;
11 
12 class Base
13 {
14 public:
15     char Value() { return 'A'; }
16     
17     virtual char VirtualValue() { return 'X'; }
18 };
19 
20 class Derived : public Base
21 {
22 public:
23     char Value() { return 'U'; }
24 };
25 
26 class VirtualDerived : virtual public Base
27 {
28 public:
29     char Value() { return 'Z'; }
30     
31     char VirtualValue() { return 'V'; }
32 };
33 
34 int main ()
35 {
36     Base * p1 = new Derived();
37     
38     Base * p2 = new VirtualDerived();
39     
40     cout << p1->Value() << " " <<
41     
42     p1->VirtualValue() << " " <<
43     
44     p2->Value() << " " <<
45     
46     p2->VirtualValue() << " " << endl;
47     
48     return 0;
49 }
main.cpp
A X A V 
Program ended with exit code: 0
View Result

類模版

  • 模版是泛型編程的基礎:經過模版能快速創建具備類型安全的類庫集合和函數集合,使用模版操做不一樣數據類型,從而避免爲每一種數據類型產生一個單獨的類或函數。
  • 類模版的做用
    • 將程序要處理的對象的類型參數化
    • 使程序能夠處理不一樣類型的對象
  • 下例演示了模版的使用。 
 1 //
 2 //  main.cpp
 3 //  LeetCode
 4 //
 5 //  Created by Hao on 2017/3/16.
 6 //  Copyright © 2017年 Hao. All rights reserved.
 7 //
 8 
 9 #include <iostream>
10 using namespace std;
11 
12 namespace nsT {
13     template <typename T>
14     
15     T min(T a, T b)
16     {
17         return (a < b) ? a : b;
18     }
19 }
20 
21 int main ()
22 {
23     int a = 10, b = 9;
24     int c = min(a, b);
25     
26     cout << c << endl;
27     
28     double d = 1.0, e = 2.0;
29     double f = min(d, e);
30     
31     cout << f << endl;
32     
33     return 0;
34 }
View Code
  • 下例演示了類模版的使用。
 1 //
 2 //  class_template.hpp
 3 //  LeetCode
 4 //
 5 //  Created by Hao on 2018/1/7.
 6 //  Copyright © 2018年 Hao. All rights reserved.
 7 //
 8 
 9 #ifndef class_template_hpp
10 #define class_template_hpp
11 
12 #include <iostream>
13 using namespace std;
14 
15 namespace nsT {
16     class Test {
17     public:
18         Test() {}
19         
20     private:
21         // Error : Calling a private constructor of class 'nsT::Test'
22 /*
23         Test(const Test &);
24         Test& operator= (const Test &);
25 */
26     };
27     
28     template <typename T>
29     class Example {
30     public:
31         Example(T val)
32             : m_val(val)
33         {
34             cout << "Example constructing\n" << endl;
35         }
36         
37         ~Example()
38         {
39             cout << "Example destructing\n" << endl;
40         }
41         
42         T get() const
43         {
44             return m_val;
45         }
46         
47         void set(T val)
48         {
49             m_val = val;
50         }
51         
52     private:
53         T   m_val;
54     };
55 } // end of namespace nsT
56 
57 #endif /* class_template_hpp */
class_template.hpp
 1 //
 2 //  main.cpp
 3 //  LeetCode
 4 //
 5 //  Created by Hao on 2017/3/16.
 6 //  Copyright © 2017年 Hao. All rights reserved.
 7 //
 8 
 9 #include <iostream>
10 using namespace std;
11 
12 #include "class_template.hpp"
13 
14 using namespace nsT;
15 
16 int main ()
17 {
18     // Using int as data type
19     Example<int>    val(1);
20     
21     int data = val.get();
22     
23     cout << "data = " << data << "\n" << endl;
24     
25     // Using double as data type
26     Example<double> val2(2.0);
27     
28     cout << val2.get() << "\n" << endl;
29     
30     Test    test;
31     
32     // Using class as data type
33     Example<Test>   val3(test);
34     
35     return 0;
36 }
main.cpp
  • 類模版與模版類的區別
    • 類模版是模版的定義,不是一個實實在在的類,定義中用到通用類型參數。
    • 模版類是實實在在的類定義,是類模版的實例化。類定義中參數被實際類型所代替。
  • 下例演示了用類模版來定義一個通用堆棧,此時該通用堆棧還不是一個類定義,只是類定義的一個框架,即類模版。
 1 //
 2 //  Stack.hpp
 3 //  LeetCode
 4 //
 5 //  Created by Hao on 2018/1/8.
 6 //  Copyright © 2018年 Hao. All rights reserved.
 7 //
 8 
 9 #ifndef Stack_hpp
10 #define Stack_hpp
11 
12 #include <iostream>
13 using namespace std;
14 
15 namespace nsT {
16     template<typename T>    class StackIterator;    // 前置聲明
17     
18     template<typename T>
19     class Stack
20     {
21     public:
22         Stack()
23             : m_top(0)
24         {
25             cout << "Stack constructing\n" << endl;
26             
27             m_array[0] = T();
28         }
29         
30         ~Stack()
31         {
32             cout << "Stack destructing\n" << endl;
33         }
34         
35         int size() const
36         {
37             return m_top;
38         }
39         
40         void push(const T&);
41         T pop();
42         friend class StackIterator<T>;  // 友元類
43         
44     private:
45         enum { SIZE = 100 };
46         T   m_array[SIZE];
47         int m_top;
48     };
49     
50     template<typename T>
51     void Stack<T>::push(const T& val)
52     {
53         if (m_top < SIZE) {
54             m_array[m_top ++] = val;
55         }
56     }
57     
58     template<typename T>
59     T Stack<T>::pop()
60     {
61         if (m_top > 0)
62             return m_array[-- m_top];
63         else
64             return m_array[0];
65     }
66     
67     template<typename T>
68     class StackIterator {
69     public:
70         StackIterator(Stack<T>& val)
71             : m_stack(val), m_index(0)
72         {
73             cout << "StackIterator constructing\n" << endl;
74         }
75         
76         ~StackIterator()
77         {
78             cout << "StackIterator destructing\n" << endl;
79         }
80         
81         T& operator++ (int) // post
82         {
83             int ret = m_index;
84             
85             if (m_index < m_stack.m_top - 1)
86                 m_index ++;
87             
88             return m_stack.m_array[ret];
89         }
90         
91     private:
92         Stack<T>&   m_stack;
93         int         m_index;
94     };
95 }
96 #endif /* Stack_hpp */
Stack.hpp
 1 //
 2 //  main.cpp
 3 //  LeetCode
 4 //
 5 //  Created by Hao on 2017/3/16.
 6 //  Copyright © 2017年 Hao. All rights reserved.
 7 //
 8 
 9 #include <iostream>
10 using namespace std;
11 
12 #include "Stack.hpp"
13 
14 using namespace nsT;
15 
16 int main ()
17 {
18     Stack<int>  stack;
19 
20     stack.push(1);
21     stack.push(2);
22     stack.push(3);
23 
24     auto size = stack.size();
25     
26     cout << "size of stack : " << size << "\n" << endl;
27     
28     StackIterator<int>  iter(stack);
29     
30     for (auto i = 0; i < size; i ++)
31         cout << iter ++ << "\n" << endl;
32         
33     cout << "pop : " << stack.pop() << "\n" << endl;
34     
35     return 0;
36 }
main.cpp

STL

  • 將算法從特定的數據結構中抽象出來 
  • C++的模版爲泛型程序設計奠基了關鍵的基礎
  • STL是泛型程序設計的一個範例,由一些可適應不一樣需求的集合類以及在這些數據集合上操做的算法構成
  • STL(模板庫)_百度百科
    • https://baike.baidu.com/item/STL/70103?fr=aladdin
  • 容器的類別
    • 序列式容器:排列次序取決於插入時機和位置
    • 關聯式容器:排列順序取決於特定準則(平衡二叉樹)
  • 向量(vector)屬於序列式容器,用於容納不定長的線性序列,提供對序列的快速隨機訪問(直接訪問)。
  • 向量是動態結構,模擬動態數組,它的大小不固定,能夠在程序運行時增長或減小。
  • vector的元素能夠是任意類型,但必須具有賦值和拷貝能力,具備public的拷貝構造函數和重載的賦值運算符。
  • vector的大小(size)和容量(capacity)一般是不一樣的,capacity返回vector實際能容納的元素數量。若是超過這個數量須要從新配置內部存儲器。
  • vector(Java與C++語言中的對象)_百度百科
    • https://baike.baidu.com/item/vector/3330482?fr=aladdin
  • 下例演示了vector的使用
 1 //
 2 //  main.cpp
 3 //  LeetCode
 4 //
 5 //  Created by Hao on 2017/3/16.
 6 //  Copyright © 2017年 Hao. All rights reserved.
 7 //
 8 
 9 #include <iostream>
10 #include <vector>
11 using namespace std;
12 
13 int main()
14 {
15     vector<int> coll;
16     
17     cout << "capacity : " << coll.capacity() << "\n" << endl;
18     cout << "size : " << coll.size() << "\n" << endl;
19     
20     for (auto i = 0; i < 10; i ++)
21         coll.push_back(i);
22     
23     cout << "capacity : " << coll.capacity() << "\n" << endl;
24     cout << "size : " << coll.size() << "\n" << endl;
25 
26     cout << coll[9] << endl;
27     
28     return 0;
29 }
vector.cpp
capacity : 0

size : 0

capacity : 16

size : 10

9
Program ended with exit code: 0
View Result
  • 迭代器是面向對象版本的指針
    • 指針能夠指向內存中的一個地址
    • 迭代器能夠指向容器中的一個位置,用來遍歷STL容器的所有或部分元素
  • STL中的每個容器類模版中,都定義了一組對應的迭代器類。 
  • 因此容器都提供兩種迭代器
    • Container::iterator以「讀寫」模式遍歷元素
    • Container::const_iterator以「只讀」模式遍歷元素
  • 由end操做返回的迭代指向vector的「末端元素的下一個」。一般稱爲超出末端迭代器(off-the-end iterator)。
 1 //
 2 //  main.cpp
 3 //  LeetCode
 4 //
 5 //  Created by Hao on 2017/3/16.
 6 //  Copyright © 2017年 Hao. All rights reserved.
 7 //
 8 
 9 #include <iostream>
10 #include <vector>
11 using namespace std;
12 
13 void print_array(const vector<int> & array)
14 {
15     vector<int>::const_iterator   iter;
16     
17     cout << "array[";
18     
19     for (iter = array.begin(); iter != array.end(); ++ iter)
20         cout << *iter << " , ";
21         
22     cout << "]\n" << endl;
23 }
24 
25 int main()
26 {
27     vector<int>                 array;
28     vector<int>::iterator       iter;
29     vector<int>::const_iterator citer;
30     
31     array.push_back(42);
32     array.push_back(1);
33     array.push_back(100);
34     
35     array.pop_back();
36     
37     iter = array.begin();
38     
39     cout << *iter << "\n" << endl;
40     
41     *iter = 109;
42     
43     citer = array.begin();
44     
45     cout << *citer << "\n" << endl;
46     
47     //    *citer = 20; // Error : Cannot assign to return value because function 'operator*' returns a const value
48     
49     print_array(array);
50     
51     return 0;
52 }
vector_iterator.cpp
42

109

array[109 , 1 , ]

Program ended with exit code: 0
View Result
  •  STL中的排序算法sort模版有兩種:一種以升序排列,另外一種用自定義的_compare函數代替operator<(x,y)。
  • 下例演示了sort函數的使用。
 1 //
 2 //  main.cpp
 3 //  LeetCode
 4 //
 5 //  Created by Hao on 2017/3/16.
 6 //  Copyright © 2017年 Hao. All rights reserved.
 7 //
 8 
 9 #include <iostream>
10 #include <vector>
11 using namespace std;
12 
13 bool compare(const int & a, const int & b)
14 {
15     return a > b;
16 }
17 
18 void print_array(const vector<int> & array)
19 {
20     vector<int>::const_iterator   iter;
21     
22     cout << "array[";
23     
24     for (iter = array.begin(); iter != array.end(); ++ iter)
25         cout << *iter << " , ";
26     
27     cout << "]\n" << endl;
28 }
29 
30 int main()
31 {
32     vector<int>                 array;
33     vector<int>::iterator       iter;
34     vector<int>::const_iterator citer;
35     
36     array.push_back(42);
37     array.push_back(1);
38     array.push_back(100);
39     
40     print_array(array);
41 
42     sort(array.begin(), array.end());
43     
44     cout << "After sort(array.begin(), array.end())\n" << endl;
45     
46     print_array(array);
47     
48     sort(array.begin(), array.end(), compare);
49 
50     cout << "After sort(array.begin(), array.end(), compare)\n" << endl;
51     
52     print_array(array);
53 
54     return 0;
55 }
sort_int.cpp
array[42 , 1 , 100 , ]

After sort(array.begin(), array.end())

array[1 , 42 , 100 , ]

After sort(array.begin(), array.end(), compare)

array[100 , 42 , 1 , ]

Program ended with exit code: 0
View Result
 1 //
 2 //  main.cpp
 3 //  LeetCode
 4 //
 5 //  Created by Hao on 2017/3/16.
 6 //  Copyright © 2017年 Hao. All rights reserved.
 7 //
 8 
 9 #include <iostream>
10 #include <vector>
11 #include <string>
12 using namespace std;
13 
14 class ID
15 {
16 public:
17     ID() : m_name(""), m_score(0) {}
18     ID(string name, int score) : m_name(name), m_score(score){}
19     
20     string  m_name;
21     int     m_score;
22 };
23 
24 bool operator== (const ID & x, const ID & y)
25 {
26     return (x.m_name == y.m_name) && (x.m_score == y.m_score);
27 }
28 
29 bool operator< (const ID & x, const ID & y)
30 {
31     return x.m_score < y.m_score;
32 }
33 
34 bool compare(const ID & x, const ID & y)
35 {
36     return x.m_score > y.m_score;
37 }
38 
39 int main()
40 {
41     vector<ID>              ids;
42     vector<ID>::iterator    iter;
43     
44     ids.push_back(ID("Tom", 5));
45     ids.push_back(ID("John", 1));
46     ids.push_back(ID("Alex", 2));
47     
48     // By default
49     sort(ids.begin(), ids.end());
50     
51     cout << "After sort(ids.begin(), ids.end())\n" << endl;
52     
53     for (iter = ids.begin(); iter != ids.end(); ++ iter)
54     {
55         cout << (*iter).m_name << " : " << (*iter).m_score << endl;
56     }
57 
58     cout << endl;
59     
60     // With compare function
61     sort(ids.begin(), ids.end(), compare);
62 
63     cout << "After sort(ids.begin(), ids.end(), compare)\n" << endl;
64     
65     for (iter = ids.begin(); iter != ids.end(); ++ iter)
66     {
67         cout << (*iter).m_name << " : " << (*iter).m_score << endl;
68     }
69 
70     return 0;
71 }
sort_class.cpp
After sort(ids.begin(), ids.end())

John : 1
Alex : 2
Tom : 5

After sort(ids.begin(), ids.end(), compare)

Tom : 5
Alex : 2
John : 1
Program ended with exit code: 0
View Result
  • 列表list使用雙向鏈表管理元素
  • List的元素能夠是任意類型,但必須具有賦值和拷貝能力
  • List不支持隨機存取,不提供下表操做符
  • 在任何位置上指向元素的插入和刪除效率高 
  • list - C++ Reference
  • list是順序訪問的容器
  • list也有insert函數,它不會使任何迭代子或引用變得無效。
  • 鏈式結構最大的優勢是序列便於重組,插入或刪除鏈表中對象時無需移動其餘對象的存儲位置。
 1 //
 2 //  main.cpp
 3 //  LeetCode
 4 //
 5 //  Created by Hao on 2017/3/16.
 6 //  Copyright © 2017年 Hao. All rights reserved.
 7 //
 8 
 9 #include <iostream>
10 #include <list>
11 using namespace std;
12 
13 bool compare(const int & a, const int & b)
14 {
15     return a > b;
16 }
17 
18 void show_list(const list<int> & coll)
19 {
20     list<int>::const_iterator   citer;
21     
22     for (citer = coll.begin(); citer != coll.end(); ++ citer)
23         cout << *citer << endl;
24 }
25 
26 int main()
27 {
28     list<int>   coll;
29     
30     coll.push_back(10); // 10
31     coll.push_back(11); // 10, 11
32     coll.push_front(12);    // 12, 10, 11
33     coll.push_front(9);     // 9, 12, 10, 11
34     
35     list<int>::iterator iter = coll.begin();    // 9
36     
37     coll.erase(iter);   // 12, 10, 11
38     
39     iter ++;    // 12
40     
41     coll.erase(iter);   // 10, 11
42     
43     coll.push_back(2);  // 10, 11, 2
44     coll.push_back(1);  // 10, 11, 2, 1
45 
46     // Sort in ascending order
47     coll.sort();
48 
49     cout << "After coll.sort() : \n" << endl;
50     
51     show_list(coll);
52     
53     cout << endl;
54     
55     // Sort in descending order
56     coll.sort(compare);
57     
58     cout << "After coll.sort(compare) : \n" << endl;
59 
60     show_list(coll);
61 
62     return 0;
63 }
list.cpp
After coll.sort() : 

1
2
10
11

After coll.sort(compare) : 

11
10
2
1
Program ended with exit code: 0
View Result
  • 集合set與映射map是兩種主要的非線性容器類
    • 內部實現通常爲平衡二叉樹(balanced binary tree)
  • set - C++ Reference
    • http://www.cplusplus.com/reference/set/set/ 
 1 //
 2 //  main.cpp
 3 //  LeetCode
 4 //
 5 //  Created by Hao on 2017/3/16.
 6 //  Copyright © 2017年 Hao. All rights reserved.
 7 //
 8 
 9 #include <iostream>
10 #include <set>
11 using namespace std;
12 
13 int main()
14 {
15     set<int>            coll;
16     set<int>::iterator  iter;
17     
18     coll.insert(1);
19     coll.insert(100);
20     coll.insert(90);
21     coll.insert(-1);
22     coll.insert(80);
23     
24     for (iter = coll.begin(); iter != coll.end(); ++ iter) {
25         cout << *iter << endl;
26     }
27 
28     return 0;
29 }
set.cpp
-1
1
80
90
100
Program ended with exit code: 0
View Result
  • map是STL的一個關聯容器,它提供一對一的數據處理能力,爲鍵值對。
    • 其中第一個爲關鍵字key,每一個key只能在map中出現一次
    • 第二個爲該關鍵字的值value 
  • map - C++ Reference
    • http://www.cplusplus.com/reference/map/map/
  • map數據的插入
    • 用insert函數插入pair數據
    • 用insert函數插入value_type數據
    • 用insert函數插入make_pair數據
      • make_pair - C++ Reference
        • http://www.cplusplus.com/reference/utility/make_pair/
    • 直接用下標訪問賦值
  • map的大小:size()函數
  • 數據的遍歷:迭代器iterator
  • unordered_map內部實現使用hash表,效率更高
 1 //
 2 //  main.cpp
 3 //  LeetCode
 4 //
 5 //  Created by Hao on 2017/3/16.
 6 //  Copyright © 2017年 Hao. All rights reserved.
 7 //
 8 
 9 #include <iostream>
10 #include <map>
11 #include <string>
12 using namespace std;
13 
14 class Compare
15 {
16 public:
17     // Overload operator(), must be wtih "const"
18     bool operator() (const int & a, const int & b) const
19     {
20         return a > b;
21     }
22 };
23 
24 int main()
25 {
26     map<int, string>    coll;
27     map<int, string>::iterator  iter;
28     
29     coll.insert(pair<int, string>(9, "Nine"));
30     coll.insert(map<int, string>::value_type(1, "One"));
31     coll.insert(make_pair(2, "Two"));
32     coll[0] = "Zero";
33     
34     cout << "Sort in default ascending order\n" << endl;
35     
36     for (iter = coll.begin(); iter != coll.end(); ++ iter)
37         cout << iter->first << " : " << iter->second << endl;
38 
39     cout << endl;
40     
41     // Sort with overloaded Compare
42     map<int, string, Compare>    coll_cmp;
43     map<int, string, Compare>::iterator iter_cmp;
44     
45     coll_cmp.insert(pair<int, string>(9, "Nine"));
46     coll_cmp.insert(map<int, string>::value_type(1, "One"));
47     coll_cmp.insert(make_pair(2, "Two"));
48     coll_cmp[0] = "Zero";
49     
50     cout << "Sort in descending order with overloaded Compare operator\n" << endl;
51     
52     for (iter_cmp = coll_cmp.begin(); iter_cmp != coll_cmp.end(); ++ iter_cmp)
53         cout << iter_cmp->first << " : " << iter_cmp->second << endl;
54 
55     return 0;
56 }
map.cpp
Sort in default ascending order

0 : Zero
1 : One
2 : Two
9 : Nine

Sort in descending order with overloaded Compare operator

9 : Nine
2 : Two
1 : One
0 : Zero
Program ended with exit code: 0
View Result

智能指針 

  • 使用類模版來管理指針,建立模版類管理指針,實現資源的管理。
  • 下例實現了智能指針。注意它不是線程安全的。
  • #pragma once_百度百科
    • https://baike.baidu.com/item/%23pragma%20once
    • #pragma once是 編譯器相關的,有的編譯器支持,有的編譯器不支持,具體狀況請查看編譯器API文檔,不過如今大部分編譯器都有這個雜注了。
    • #ifndef,#define,#endif是C/C++語言中的宏定義,經過宏定義避免文件屢次編譯。因此在全部支持C++語言的編譯器上都是有效的,若是寫的程序要跨平臺,最好使用這種方式。
 1 //
 2 //  RefCount.h
 3 //  LeetCode
 4 //
 5 //  Created by Hao on 2018/1/14.
 6 //  Copyright © 2018年 Hao. All rights reserved.
 7 //
 8 
 9 #ifndef RefCount_h
10 #define RefCount_h
11 
12 //#pragma once
13 
14 #include <stdio.h>
15 
16 #define TRACE printf
17 
18 class RefCount
19 {
20 public:
21     RefCount(void)
22     {
23         TRACE("RefCount constructing\n\n");
24         crefs = 0;
25     }
26     
27     virtual ~RefCount(void)
28     {
29         TRACE("RefCount destructing\n\n");
30     }
31     
32     void upcount(void)
33     {
34         ++ crefs;
35         TRACE("up to %d\n\n", crefs);
36     }
37     
38     void downcount(void)
39     {
40         if (-- crefs == 0) {
41             TRACE("down to %d\n\n", crefs);
42             delete this;
43         } else {
44             TRACE("down to %d\n\n", crefs);
45         }
46     }
47     
48 private:
49     int crefs;
50 };
51 
52 class Sample : public RefCount
53 {
54 public:
55     Sample()
56     {
57         TRACE("Sample constructing\n\n");
58     }
59     
60     ~Sample()
61     {
62         TRACE("Sample destructing\n\n");
63     }
64     
65     void doSomething(void)
66     {
67         printf("Did something\n\n");
68     }
69 };
70 
71 #endif /* RefCount_h */
RefCount.h
 1 //
 2 //  SmartPtr.h
 3 //  LeetCode
 4 //
 5 //  Created by Hao on 2018/1/14.
 6 //  Copyright © 2018年 Hao. All rights reserved.
 7 //
 8 
 9 #ifndef SmartPtr_h
10 #define SmartPtr_h
11 
12 #include <stdio.h>
13 
14 #define TRACE printf
15 
16 /*
17 //Compiler switch
18 #ifdef TRACE_SMPTR
19 #define TRACE printf
20 #else
21 #define TRACE
22 #endif
23 */
24 
25 template <typename T>
26 class SmartPtr
27 {
28 public:
29     SmartPtr(T* p_) : m_p(p_)
30     {
31         TRACE("SmartPtr constructing\n\n");
32         m_p->upcount();
33     }
34     
35     ~SmartPtr(void)
36     {
37         TRACE("SmartPtr destructing\n\n");
38         m_p->downcount();
39     }
40     
41     operator T* (void)
42     {
43         TRACE("%s, %d\n\n", __func__, __LINE__);
44         return m_p;
45     }
46     
47     T& operator* (void)
48     {
49         TRACE("%s, %d\n\n", __func__, __LINE__);
50         return *m_p;
51     }
52     
53     T* operator-> (void)
54     {
55         TRACE("%s, %d\n\n", __func__, __LINE__);
56         return m_p;
57     }
58     
59     SmartPtr& operator= (SmartPtr<T> &p_)
60     {
61         TRACE("%s, %d\n\n", __func__, __LINE__);
62         return operator= ((T*)p_);
63     }
64     
65     SmartPtr& operator= (T* p_)
66     {
67         TRACE("%s, %d\n\n", __func__, __LINE__);
68         p_->upcount();
69         m_p->downcount();
70         m_p = p_;
71         
72         return *this;
73     }
74     
75     SmartPtr(const SmartPtr<T> &p_)
76     {
77         TRACE("%s, %d\n\n", __func__, __LINE__);
78         m_p = p_.m_p;
79         m_p->upcount();
80     }
81     
82 private:
83     T*  m_p;
84 };
85 
86 #endif /* SmartPtr_h */
SmartPtr.h
 1 //
 2 //  main.cpp
 3 //  LeetCode
 4 //
 5 //  Created by Hao on 2017/3/16.
 6 //  Copyright © 2017年 Hao. All rights reserved.
 7 //
 8 
 9 #include "RefCount.h"
10 #include "SmartPtr.h"
11 #include <iostream>
12 using namespace std;
13 
14 int main(int argc, char* argv[])
15 {
16     cout << "SmartPtr<Sample> p = new Sample; --->\n" << endl;
17     
18     SmartPtr<Sample> p = new Sample; // Sample*
19     
20     cout << "SmartPtr<Sample> p2 = new Sample; --->\n" << endl;
21     
22     SmartPtr<Sample> p2 = new Sample;
23 
24     cout << "p = p2; --->\n" << endl;
25     
26     p = p2;
27     
28     cout << "p->doSomething(); --->\n" << endl;
29     
30     p->doSomething();
31     
32     cout << "(*p).doSomething(); --->\n" << endl;
33     
34     (*p).doSomething();
35     
36     return 0;
37 }
main.cpp
SmartPtr<Sample> p = new Sample; --->

RefCount constructing

Sample constructing

SmartPtr constructing

up to 1

SmartPtr<Sample> p2 = new Sample; --->

RefCount constructing

Sample constructing

SmartPtr constructing

up to 1

p = p2; --->

operator=, 61

operator Sample *, 43

operator=, 67

up to 2

down to 0

Sample destructing

RefCount destructing

p->doSomething(); --->

operator->, 55

Did something

(*p).doSomething(); --->

operator*, 49

Did something

SmartPtr destructing

down to 1

SmartPtr destructing

down to 0

Sample destructing

RefCount destructing

Program ended with exit code: 0
View Result 

設計模式

設計模式簡介

  • 設計模式實際上討論的是在解決面向對象設計的某類問題時,應該設計哪些類,這些類之間如何通訊。
  • 設計模式能夠幫助設計者更快更好的完成系統設計。 

單例設計模式

  • 保證在應用程序中,一個類只有一個對象
  • 將構造函數設置爲私有,在類的實現中確保生成對象的個數
  • 下例演示了singleton的實現,須要把構造函數/析構函數/拷貝構造函數/賦值運算符重載都聲明爲私有,再使用public函數來控制類的構造和析構,從而實現一個類只有一個對象
 1 //
 2 //  Singleton.hpp
 3 //  LeetCode
 4 //
 5 //  Created by Hao on 2017/12/27.
 6 //  Copyright © 2017年 Hao. All rights reserved.
 7 //
 8 
 9 #ifndef Singleton_hpp
10 #define Singleton_hpp
11 
12 class Singleton
13 {
14 public:
15     static Singleton* getInstance();
16     void doSomething();
17     void destroy();
18     
19 private:
20     Singleton();
21     ~Singleton();
22     Singleton(const Singleton &);
23     Singleton& operator=(const Singleton &);
24     
25     static Singleton *instance;
26 };
27 
28 #endif /* Singleton_hpp */
Singleton.hpp
 1 //
 2 //  Singleton.cpp
 3 //  LeetCode
 4 //
 5 //  Created by Hao on 2017/12/27.
 6 //  Copyright © 2017年 Hao. All rights reserved.
 7 //
 8 
 9 #include "Singleton.hpp"
10 
11 #include <iostream>
12 using namespace std;
13 
14 // 靜態成員變量須要在類外初始化
15 Singleton* Singleton::instance = nullptr;
16 
17 Singleton::Singleton()
18 {
19     cout << "Constructing Singleton instance --->\n"  << endl;
20 }
21 
22 Singleton::~Singleton()
23 {
24     cout << "Destructing Singleton instance --->\n" << endl;
25 }
26 
27 // Not thread safe,
28 // Need to use pthread_mutex_lock/unlock
29 Singleton* Singleton::getInstance()
30 {
31     Singleton* ret = instance;
32     
33     if (instance == nullptr) {
34         instance = new Singleton();
35         ret = instance;
36     }
37     
38     return ret;
39 }
40 
41 void Singleton::doSomething()
42 {
43     cout << __func__ << " , LINE " << __LINE__ << "\n" << endl;
44 }
45 
46 void Singleton::destroy()
47 {
48     delete this;
49     instance = nullptr;
50 }
Singleton.cpp
 1 //
 2 //  main.cpp
 3 //  LeetCode
 4 //
 5 //  Created by Hao on 2017/3/16.
 6 //  Copyright © 2017年 Hao. All rights reserved.
 7 //
 8 
 9 #include "Singleton.hpp"
10 
11 #include <iostream>
12 using namespace std;
13 
14 int main ()
15 {
16     /*
17     Singleton* val = new Singleton();   // ERROR : Calling a private constructor of class 'Singleton'
18     delete val;                         // ERROR : Calling a private destructor of class 'Singleton'
19     val = nullptr;
20     */
21     
22     Singleton::getInstance()->doSomething();
23     
24     Singleton::getInstance()->destroy();
25     
26     return 0;
27 }
main.cpp
Constructing Singleton instance --->

doSomething , LINE 43

Destructing Singleton instance --->

Program ended with exit code: 0
View Result

簡單工廠模式

  • 使用簡單工廠模式實現開閉原則
  • 簡單工廠模式_百度百科
    • https://baike.baidu.com/item/簡單工廠模式
    • 簡單工廠模式是屬於建立型模式,又叫作靜態工廠方法(Static Factory Method)模式,但不屬於23種GOF設計模式之一。簡單工廠模式是由一個工廠對象決定建立出哪種產品類的實例。簡單工廠模式是工廠模式家族中最簡單實用的模式,能夠理解爲是不一樣工廠模式的一個特殊實現。
    • 簡單工廠模式的實質是由一個工廠類根據傳入的參數,動態決定應該建立哪個產品類(這些產品類繼承自一個父類或接口)的實例。
    • 該模式中包含的角色及其職責
      • 工廠(Creator)角色:簡單工廠模式的核心,它負責實現建立全部實例的內部邏輯。工廠類的建立產品類的方法能夠被外界直接調用,建立所需的產品對象。
      • 抽象產品(Product)角色:簡單工廠模式所建立的全部對象的父類,它負責描述全部實例所共有的公共接口。
      • 具體產品(Concrete Product)角色:是簡單工廠模式的建立目標,全部建立的對象都是充當這個角色的某個具體類的實例。
    • 使用場景
      • 工廠類負責建立的對象比較少;
      • 客戶只知道傳入工廠類的參數,對於如何建立對象(邏輯)不關心;
      • 因爲簡單工廠很容易違反高內聚責任分配原則,所以通常只在很簡單的狀況下應用。
  • 下例演示了簡單工廠模式應用。注意接口類純虛函數的析構函數須要定義,類的前置聲明。
 1 //
 2 //  Fruit.hpp
 3 //  LeetCode
 4 //
 5 //  Created by Hao on 2018/1/5.
 6 //  Copyright © 2018年 Hao. All rights reserved.
 7 //
 8 
 9 #ifndef Fruit_hpp
10 #define Fruit_hpp
11 
12 #include <iostream>
13 using namespace std;
14 
15 class Fruit
16 {
17 public:
18     virtual ~Fruit() = 0; // 接口類純虛函數的析構函數須要定義
19     virtual void plant() = 0;
20     virtual void grow() = 0;
21     virtual void harvest() = 0;
22 };
23 
24 class Apple : public Fruit
25 {
26 public:
27     Apple();
28     ~Apple();
29     
30     void plant();
31     void grow();
32     void harvest();
33 };
34 
35 class Grape : public Fruit
36 {
37 public:
38     Grape();
39     ~Grape();
40     
41     void plant();
42     void grow();
43     void harvest();
44 };
45 
46 enum {
47     APPLE = 0,
48     GRAPE = 1,
49     ORANGE = 2
50 };
51 
52 // 前置聲明
53 class Orange;
54 
55 class Gardener
56 {
57 public:
58     Gardener();
59     ~Gardener();
60     
61     // 0 apple, 1 grape, orange
62     Fruit * getFruit(int);
63     
64 private:
65     Apple * m_apple;
66     Grape * m_grape;
67     Orange * m_orange;
68 };
69 
70 class Orange : public Fruit
71 {
72 public:
73     Orange();
74     ~Orange();
75     
76     void plant();
77     void grow();
78     void harvest();
79 };
80 
81 #endif /* Fruit_hpp */
Fruit.hpp
  1 //
  2 //  Fruit.cpp
  3 //  LeetCode
  4 //
  5 //  Created by Hao on 2018/1/5.
  6 //  Copyright © 2018年 Hao. All rights reserved.
  7 //
  8 
  9 #include "Fruit.hpp"
 10 #include <iostream>
 11 using namespace std;
 12 
 13 Fruit::~Fruit()
 14 {
 15     cout << "Fruit destructing\n" << endl;
 16 }
 17 
 18 Apple::Apple()
 19 {
 20     cout << "Apple constructing\n" << endl;
 21 }
 22 
 23 Apple::~Apple()
 24 {
 25     cout << "Apple destructing\n" << endl;
 26 }
 27 
 28 void Apple::plant()
 29 {
 30     cout << "Apple planting\n" << endl;
 31 }
 32 
 33 void Apple::grow()
 34 {
 35     cout << "Apple growing\n" << endl;
 36 }
 37 
 38 void Apple::harvest()
 39 {
 40     cout << "Apple harvesting\n" << endl;
 41 }
 42     
 43 Grape::Grape()
 44 {
 45     cout << "Grape constructing\n" << endl;
 46 }
 47 
 48 Grape::~Grape()
 49 {
 50     cout << "Grape destructing\n" << endl;
 51 }
 52 
 53 void Grape::plant()
 54 {
 55     cout << "Grape planting\n" << endl;
 56 }
 57 
 58 void Grape::grow()
 59 {
 60     cout << "Grape growing\n" << endl;
 61 }
 62 
 63 void Grape::harvest()
 64 {
 65     cout << "Grape harvesting\n" << endl;
 66 }
 67 
 68 Orange::Orange()
 69 {
 70     cout << "Orange constructing\n" << endl;
 71 }
 72 
 73 Orange::~Orange()
 74 {
 75     cout << "Orange destructing\n" << endl;
 76 }
 77 
 78 void Orange::plant()
 79 {
 80     cout << "Orange planting\n" << endl;
 81 }
 82 
 83 void Orange::grow()
 84 {
 85     cout << "Orange growing\n" << endl;
 86 }
 87 
 88 void Orange::harvest()
 89 {
 90     cout << "Orange harvesting\n" << endl;
 91 }
 92 
 93 Gardener::Gardener()
 94 {
 95     cout << "Gardener constructing\n" << endl;
 96     
 97     m_apple = nullptr;
 98     m_grape = nullptr;
 99     m_orange = nullptr;
100 }
101 
102 Gardener::~Gardener()
103 {
104     cout << "Gardener destructing\n" << endl;
105     
106     if (m_apple != nullptr) {
107         delete m_apple;
108         m_apple = nullptr;
109     }
110     
111     if (m_grape != nullptr) {
112         delete m_grape;
113         m_grape = nullptr;
114     }
115 
116     if (m_orange != nullptr) {
117         delete m_orange;
118         m_orange = nullptr;
119     }
120 }
121 
122 Fruit * Gardener::getFruit(int type)
123 {
124     Fruit * fruit = nullptr;
125     
126     if (APPLE == type) {
127         if (nullptr == m_apple)
128             m_apple = new Apple();
129         
130         fruit = m_apple;
131     } else if (GRAPE == type) {
132         if (nullptr == m_grape)
133             m_grape = new Grape();
134         
135         fruit = m_grape;
136     } else if (ORANGE == type) {
137         if (nullptr == m_orange)
138             m_orange = new Orange();
139         
140         fruit = m_orange;
141     }
142     
143     return fruit;
144 }
Fruit.cpp
 1 //
 2 //  main.cpp
 3 //  LeetCode
 4 //
 5 //  Created by Hao on 2017/3/16.
 6 //  Copyright © 2017年 Hao. All rights reserved.
 7 //
 8 
 9 #include "Fruit.hpp"
10 
11 int main ()
12 {
13     Gardener Tom;
14 
15     Fruit * fruit = Tom.getFruit(APPLE);
16     
17     fruit->plant();
18     fruit->grow();
19     fruit->harvest();
20 
21     cout << "------New request for ORANGE------\n" << endl;
22     
23     fruit = Tom.getFruit(ORANGE);
24     
25     fruit->plant();
26     fruit->grow();
27     fruit->harvest();
28     
29     return 0;
30 }
main.cpp
Gardener constructing

Apple constructing

Apple planting

Apple growing

Apple harvesting

------New request for ORANGE------

Orange constructing

Orange planting

Orange growing

Orange harvesting

Gardener destructing

Apple destructing

Fruit destructing

Orange destructing

Fruit destructing

Program ended with exit code: 0
View Result
相關文章
相關標籤/搜索