c++ programming notes
-
除錯技巧與基本知識:
-
GCC 基本知識
gcc 與 g++ 分別是 GNU 的 c & c++ 編譯器 gcc/g++
在執行編譯作業的時候,總共需要4步:-
preprocessing(編譯預處理)
-
compilation(編譯)
#compiles and assembles files but doesn’t link them. $ g++ -c #This is useful when building large projects to separate file compilation and minimize what is re-compiled.-
assembly(組裝)
-
linking(鏈接)
-
a quick start
#Say you have a file helloworld.C as follows : # #include <stdio.h> # int main () # { # printf("Hello World\n"); # } #You can compile and run it from the unix prompt as follows : $ g++ helloworld.c #This creates an executable called "a.out". You can run it by typing $ ./a.out #Since no executable name was specified to g++, a.out is chosen by default. Use the "-o" option to change the name : $ g++ -o helloworld helloworld.c #creates an executable called "helloworld". #using && to execute two commands one by one $ g++ debug.cpp -o debug && ./debug #notice that you can't type "debug" cause it's not a command you must type "./debug" without g++ before it #creates an executable called "debug" and then run it一些必備指令:
$ g++ debug.cpp -Wall -Wextra && ./debug | head -100 -
-
shell基本知識
-
復制粘貼:如果你按下滑鼠左鍵,沿著文本拖動滑鼠(或者雙擊一個單詞)高亮了一些文本,那么這些高亮的文本就被拷貝到了一個由 X 視窗系統(使 GUI作業的底層引擎)管理的緩沖區里面,然后按下滑鼠右鍵,這些文本就被粘貼到游標所在的位置,
-
檔案名和命令名是大小寫敏感的,
-
Linux 沒有“檔案擴展名”的概念,不像其它一些系統,可以用你喜歡的任何名字來給檔案起名,
-
大多數命令使用的選項,是由一個中劃線加上一個字符組成,例如,“-l”,但是許多命令,包括來自于 GNU 專案的命令,也支持長選項,長選項由兩個中劃線加上一個字組成,
-
注意表示法:在描述一個命令時,當有三個圓點跟在一個命令的引數后面,這意味著那個引數可以重復
-
ls (list content)
-
pwd (path work directory)
-
cd (change directory) 符號 “.” 指的是作業目錄, ”..” 指的是作業目錄的父目錄,
- cd .. (回傳父目錄)
- cd ./ (= cd)
- cd 更改作業目錄到你的home dictionary(家目錄)
- / 根目錄
- /bin 包含系統啟動和運行所必須的二進制程式
- ~ 家目錄
- /tab/ (auto fill up filename)
-
sudo (Super User Do)
-
file filename 查看檔案型別
-
less filename 查看檔案
-
An AND list has the form
#command2 is executed iff command1 returns an exit status of zero (success). $ <command1> && <command2>
-
-
VS Code 基本知識
VS Code 組織結構:全域——作業區(workspace)——檔案夾(即專案) -
wget
-
prinf()的使用
-
-
陣列傳遞:由于形式引數和實際引數是同一陣列(陣列名代表了陣列在記憶體中的起始地址(是一個指標),將陣列作為函式引數時,相當于把實參的陣列的起始地址賦給了形參的陣列的起始地址,因此這兩個陣列實際上就是同一個陣列,詳見P 120)定義的時候要加中括號,但是形式引數和實際引數都是陣列名,所以呼叫這個陳述句時不用加中括號!!!
注意二維陣列傳遞的時候需要指定第二維的個數(原因是為了告訴函式每個指標間相差幾個存盤空間)void swap(char str[], int k, int i) //使用方法:swap(string, 3, 1) -
幀:系統為函式分配的一塊記憶體空間,用于存放形式引數和函式體內定義的變數,函式運行結束時,系統識訓分配給該函式的幀,因此存放在這塊幀中的變數都消失了,
-
堆疊(Stack):函式定義的變數(包括形式引數)都創建在一個稱為堆疊的獨立記憶體區域,呼叫一個函式相當于從堆疊中分配一個新的幀,并且放在其他活動函式的幀上面,
return之后,函式對應的幀被識訓,幀中的變數就完全消失了,其他函式無法再參考,因此稱為區域變數, -
變數的作用域:
main函式中定義的變數也是區域的;一個程式塊中定義的變數在本程式塊中有效;區域程式塊是指一對大括號{}之間的一段C語言程式,如果區域變數與區域程式塊以外的變數重名,則前者優先于后者,回到程式塊以外時,程式重新進入最初定義的變數的作用范圍;變數定義出現在所有函式定義之外,稱為全域變數,保存在一個獨立記憶體區域中,永遠都不會被包含區域變數的幀覆寫,在區域變數與全域變數同名時,可以使用作用域運算子::加在變數名前,用于指代全域變數; -
變數的存盤類別:變數的存盤位置
存盤類別 資料型別 變數名表 //自動變數自動存于stack中,空間也自動被回收 //c++ 11后不用寫auto,只要寫 int a, b; auto int a, b; //自動型別推斷,au_a為int型別 int a = 10; auto au_a = a; //靜態變數,限定變數只能在某個區域使用,放在全域變數區 //1.靜態全域變數(通常是為了防止其他檔案參考修改該變數值造成混亂) static int x; //全域變數x為當前源檔案私有的,其他源檔案中不可參考它 //2.靜態區域變數(下次函式呼叫可以使用上次函式呼叫時的值) //注1:未初始化的static variable都自動初始化為0,且初值是編譯時賦的,運行時不會初始化 //注2:函式呼叫結束后靜態區域變數仍然存在,但其它函式無法參考 //注3:靜態區域變數在程式結束時消亡,先消亡靜態區域變數再消亡全域變數 static int c = 3;//函式在第二次呼叫及以后時該陳述句被忽略,繼續使用上次函式呼叫時c的空間 c = c + 1; //暫存器變數(僅限區域自動變數可放入CPU的暫存器中,加快讀取速度) //僅代表程式員的意向,若無合適暫存器則設為自動變數 //現在的編譯器自動優化 register int x; //外部變數的申明(不是定義不會分配空間,如同函式原型申明) /*在全域變數定義之前的陳述句或函式或者另一個源檔案中的函式也要使用該全域變數,則在參考之前應該用extern進行外部變數申明,在一個源檔案中參考的別的源檔案的全域變數就叫外部變數*/ /*使用外部變數要謹慎,因為修改該變數的值可能會影響另一個源檔案中函式執行的結果!!通常用static把某全域變數申明為本檔案私有的來防止被其他源檔案修改*/ extern 型別名 函式名 -
駝峰命名法
-
條件斷點;函式斷點
-
宏除錯:
#define DEBUG #ifdef DEBUG #endif DEBUG -
演算法復雜性:
輸入大小
代碼質量
硬體
演算法復雜性 -
時間復雜性:
基本操作
最壞和平均情況O
最好情況\(\Omega\)
最好情況與最壞情況相同\(\Theta\) -
空間復雜性:
S(n) -
排序:
冒泡排序O(n2)
插入排序O(n2)
希爾排序(插入的優化)
計數排序 -
參考傳遞的
swaptemplate <typename T> void swap(T &a, T &b) { T c = a; a = b; b = c; } -
最優化問題:
窮舉法
貪心法:最速下降法(區域最優);硬幣找零 -
空陳述句
-
陣列:
同類,有序,元素個數必須是常量
定義常量
陣列名存放了陣列的起始地址,除字串以外只能一個一個輸入輸出
陣列定義在一塊連續的空間上a[i] a + i //這個指標相當于a之后a的型別所定義的第i個記憶體區域C/C++不檢查陣列越界!!
-
亂數
strand(time(NULL)) -
資料的內部視圖和外部視圖
-
指標的概念
指標的型別決定了編譯器解釋地址中內容的方式
*在語法上屬于變數名,不屬于型別名
直接訪問間接訪問
地址運算子&x回傳的是變數 x 的地址,不能跟常量或運算式
*intp回傳intp指向的這個變數的內容
在對intp使用參考運算之前,必須先對intp賦值
指標+1表示陣列中指標指向元素的下一元素地址
可用*(intarray + k)參考intarray[k]
同類的指標變數之間可相互賦值,表示兩個指標指向同一記憶體空間//NULL是一個特殊指標值,稱為空指標,它的值為0,它可被用來初始化一個指標,表示不指向任何地址, //c++11引入了nullptr充當單獨的空指標常量,與任何指標型別可以發生隱式型別轉換,也可以隱式轉換為bool型(值為false)但是不存在到整型的隱式型別轉換 void f(int *); void f(int); //呼叫f(nullptr)將會呼叫函式f(int *)而不會像f(NULL)一樣呼叫f(int)陣列名是一個指標常量
void *指標變數名;為統配指標型別,可以與任何型別指標相互賦值 -
**指標與 const **:
//指向常量的指標(為防止指向的變數被修改而設計) const int *p = &x; //注意&后面只能跟變數,而且*p不能修改,但是p可以修改如p = &y; //指標常量(指向的地址固定但指向的變數的值可以變)定義時必須賦初值 int *const p = &x; //*p可以修改如*P = 20;,但是p不能修改 //指向常量的指標常量 const int *const p = &x; //不能修改其指向的地址,也不能通過它修改變數的值 -
動態記憶體分配:
對于一個指標變數p
申請動態變數:p = new int;
申請動態陣列:p = new int[x];
注意此處的 x 可以是變數
申請動態變數并初始化:int *p = new int(10)
釋放動態變數:delete p;
釋放動態陣列:delete [] p;(字符陣列可以不加方括號)
動態分配:在程式執行程序中需要新的存盤空間時,可用動態分配的方法向系統申請新的空間,當不再使用時用顯式的方法還給系統,這部分空間是從被稱為堆(Heap)的記憶體區域分配,
注意c++程式運行期間,動態變數不會消亡,必須用delete洗掉 -
例外:
除0;空間不夠;有些例外可以捕獲做處理讓程式運行下去 -
記憶體泄漏:
動態變數是通過指標間接訪問的,如果該指標被修改,這個區域就被丟失了,堆管理器認為你在繼續使用它們,但你不知道它們在哪里,這稱為記憶體泄露,
當釋放了記憶體區域,堆管理器重新識訓這些區域,而指標仍然指向堆區域,但不能再使用指標指向的這些區域,
釋放記憶體對一些程式不重要,但對有些程式很重要,如果你的程式要運行很長時間,而且存在記憶體泄漏,這樣程式會耗盡所有記憶體,直至崩潰, -
assert宏:
//檢查new操作回傳值來檢驗動態空間是否申請成功 int *p; p = new int; if (!p) { cout << "allocation failure\n"; return 1; } //用assert宏直接退出程式 #include <cassert> int *p; p = new int; assert(p != 0);//不加這一條后面一句陳述句就可能報錯,如果堆空間不夠了 *p = 20; -
初始化:
//auto與動態記憶體分配 auto p1 = new auto(10); //注意這里p1前不用加*號 //c++11 動態陣列初始化 int *p = new int[5]{1, 2, 3, 4, 5}; //若給出初值個數少于陣列元素個數剩余賦值為0 -
指標傳遞輸出引數:
void SolveQuadratic(double a, double b, double c, double *px1, double *px2); //傳入的只有x1與x2的地址,于是x1與x2可以成為函式的輸出,稱為輸出引數,設計函式原型時一般將輸入引數放在前面,把輸出引數放在后面 SolveQuadratic(a, b, c, &x1, &x2); cout << x1 << x2; -
字串作為函式的引數:
如果只讀取字串的話應該使用指向常量的字符指標int word_cnt(const char *s)
在函式體中可以通過++s的方法遍歷字串 -
回傳指標的函式:只需在函式名前加一個*號
注意回傳地址對應的變數不能是被調函式的區域變數,否則會消亡
如果回傳的指標是在函式體內new出來的,不要忘記在main函式中使用完后再delete掉
char *subString(char *s, int start, int end); -
參考型別:(實際上是一種隱式指標,但每次不用寫*號,簡便了程式書寫)
coint i; int &j = i; //必須賦初值,將j與i的地址關聯起來 int &j1 = j; //valid //參考的系結是永久的,不可修改 //常量參考變數,只能參考,不能賦值 const int &a = 1; //valid a實際上與一個值為1的臨時變數系結了 int b; const int &a = b; //valid 但是不能通過a修改b的值 //c++11拓展:范圍for陳述句,讓k以此等于陣列a的每一個元素 for (int k : a) cout << k; //valid for (int k : a) cin >> k; //invalid 這里是值傳遞 for (int &k : a) cin >> k; //valid //參考傳參 void swap(int &a, int &b); //注意實參必須為左值運算式 //參考傳參的好處之一:節省空間 int max(const int &a, const int &b); //給函式傳入一個常量,防止修改 //回傳參考的函式(可將函式呼叫作為左值) int &index(int j) {return a[j];} //注意只能參考左值 void main() { index(2) = 25; //相當于先執行&index(2) = a[2];,然后index(2) = 25; cout << index(2); } //如果不希望參考回傳值通過參考被修改,可申明為const const int &index(int j); //注意不能回傳該函式區域變數的參考 -
指標陣列:
main函式的引數int main(int argc, char *argv[]) //argc代表命令列中引數個數,每個實參都表示為一個字串,組成一個儲存一組字串的指標陣列*argv[] { return 0; } -
指向函式的指標: P 177
-
函式指標作為函式引數:
template<class T> void sort(T a[], int size, bool (*f) (T, T));//*f為一個比較函式的指標 sort(a, 10, increaseInt);//函式名作為第三個引數 sort<int>(a, 10, increaseInt);//防止編譯器無法確定第三個引數中T的型別 //選單選擇P180 //Lambda運算式 -
多維陣列
按行存盤
除錯方法:- 初始化串列
- 輸入輸出重定向
-
多級指標
-
字串
結束標記
空字串""占用一個空間存盤\0
輸入輸出:- 可以直接
cin以空格回車或Tab結束
自學實驗指導的實驗七
(注意緩沖區殘留的回車鍵,可用cin.get處理) coutcin.get- 推薦:
cin.getline(字符陣列, 陣列長度, 結束標記)注意cin.getline從鍵盤讀取到陣列長度-1,默認結束標記為回車,這樣就可以自由輸入空格啦!
- 可以直接
-
cstring:
strlen(s):回傳s的長度,不含'\0',故等于實際長度-1 -
函式:
函式宣告:引數表中的每個引數說明可以是型別,也可以是型別后面再接一個引數名,如:```c++ int max(int, int); int max(int a, int b); ```函式執行程序:現場保護
-
帶默認值的函式:一旦某個引數指定了默認值,它后面所有引數都必須有默認值
void f(int a, int b = 1, int c, int d = 2); //該申明是錯誤的最好在函式申明時指定默認值,因為引數默認值是給呼叫者使用的,而編譯器是根據函式原型申明來確定函式呼叫是否合法的,故在定義時指定默認值沒有意義,除非還充當了函式申明的作用
不同源檔案中的函式申明可以指定不同的默認值,但同一個源檔案中只能指定一個默認值, -
行內函式:用于解決呼叫較小函式時進行函式呼叫的額外開銷(如分配記憶體和回收記憶體)有點得不償失的問題
inline void function() { //definition }編譯器會將行內函式的代碼復制到呼叫處來避免函式呼叫,代價是會使代碼變長,
注意行內函式必須定義在被呼叫之前,因此必須放在頭檔案中(猜測因為可能會被其它函式呼叫,所以得先定義好編譯器才知道復制的代碼是是什么),否則編譯器不知道應該插入什么代碼,
行內函式只是對編譯器的建議,編譯器可以根據實際情況決定處理方式 -
多載函式:共用函式名
編譯器需要系結:為多載函式中的每一個取不同的內部名字 -
函式模板:
template <class T> //T為模板的形式引數,注意每個形參前都有關鍵字class或typename模板的實體化:函式模板實體化形成的函式叫模板函式
如果模板的形參沒有在函式的形參表中至少出現一次,需要顯式指定模板實參,因為編譯器不會自動推斷回傳值的型別template <class T1, class T2, class T3> T3 calc(T1 x, T2 y) {return x + y;} //呼叫程序: calc<int, char, char>(5, a); //結果為'f' -
包裝函式:給用戶用的函式
-
型別推斷:
auto ch = 'A'; //auto定義變數必須賦初值 auto a = 5, b = 5.5, c = 'A'; //錯誤,因為同一個auto序列中 變數必須推導為同一型別 int a, b; //不想用運算式值作為初值的型別推斷:declaretype(運算式) decltype (a + b) c; //編譯器不計算a + b的值,而是先推出a + b的型別再把其作為c的型別,c的初值隨機 -
型別別名:
typedef long long ll; using REAL = double; //c++11 //占用空間:sizeof運算子 sizeof(int); sizeof('a' + 15); sizeof(x); -
常量:
-
整型常量:
const long long n = 100; //n = 2LL; 0123 //八進制,Octal,縮寫OCT或O 0x7fffffff //十六進制,Hexadecimal,簡寫為hex -
實型常量:
指數e尾數 指數E尾數 1e3.3 //invalid e3 //invalid e //invalid -18.234e-10 //valid //一般視為double,若要視為float: 1.23f 或 1.23F -
字符常量:
\n //換行:移到下一行開始,編碼為10 \r //回車:回到當前行開始,編碼為13 \t //Tab(水平制表符):水平移到下一個Tab區,編碼為9 \0 //空字符 \177 //ASCLL值為OCT177對應字符,即為decimal127對應字符Delete鍵編碼 \xhh //兩位十六進制編碼 -
布爾常量:
true false -
符號常量:
通常全用用大寫字母#define PI 3.14159 //c語言定義的符號常量稱為宏,用編譯預處理指令#define來定義 #define RADIUS 3 + 5 //宏定義屬于簡單字串替換,不加括號會出問題!! const double PI = 3.14159 /*只能在定義時被賦值,不能修改,右邊運算式是任意的,而且初值可以不是編譯時能確定的*/ /*c++程式中某些值必須是編譯時的常量,即常量運算式,如陣列元素個數,為此引入constexpr來定義const expression*/ constexpr int N = 10; //只有N申明為constexpr時編譯器定義M時才會被通知N為常量,若N為const或者變數則會報錯 constexpr int M = N + 10; /*普通函式呼叫出現在const expression中編譯器會報錯,因為編譯器不會自動檢查函式呼叫結果是否為編譯時常量,為此引入constexpr函式,當constexpr函式出現在const expression中時,編譯器會檢查本次呼叫結果是否為常量,若是,通過編譯,否則報錯,注意只要求結果是否為常量,不在意常量如何得到,定義時關鍵字constexpr放在函式頭最前面*/ //valid constexpr int f1() {return 10;} constexpr int x = 1 + f1(); //invalid int f1() {return 10;} constexpr int x = 1 + f1(); //constexpr函式的返回傳值可以不是常量,只要不用于const expression中就行,否則會報錯 /*constexpr函式中只能有一個執行實際操作的陳述句:唯一一條return陳述句,不允許有其他執行操作的陳述句,可以有型別別名,空陳述句等,編譯時編譯器會將函式呼叫替換成函式的回傳值,因此必須在編譯時進入函式而不發生函式呼叫,因此constexpr函式一定會被隱式指定為inline函式,編譯時會將代碼復制過來運行,因此也必須放到頭檔案里(其它函式可能會呼叫,因此必須預先定義)*/ constexpr int f2(int n) {return (n % 2) ? n + 10 : 11;} //valid constexpr int f2(int n) {if (n % 2) return n + 10 else return 11;} //invalid
-
-
尾置回傳型別:回傳值型別取決于呼叫時實際引數型別時,為了讓編譯器自動推導,而不用顯示指定形參模板,c++11引入了新的函式申明語法:尾置回傳型別
template<class Type1, class Type2> auto cal(Type1 alpha, Type2 beta)->decltype(alpha + beta) {return alpha + beta;} //以下invalid,因為編譯器無法在編譯時確定alpha + beta的型別(因為還未定義) template<class Type1, class Type2> decltype(alpha + beta) cal(Type1 alpha, Type2 beta) {return alpha + beta;} -
排序進階
求單調函式反函式
堆:紅色第四章、第五章 -
toupper(ch):小寫轉大寫,大寫不變
#include <ctype.h> -
目標代碼:object code: In computing, object code or object module is the product of a compiler. Object files can in turn be linked to form an executable file or library file. An assembler is used to convert assembly code into machine code (object code). A linker links several object (and library) files to generate an executable. Assemblers can also assemble directly to machine code executable files without the object intermediary step.
-
priority queue
-
Encapsulation(封裝):the bundling of data with the methods that operate on that data, or the restricting of direct access to some of an object's components
Publicly accessible methods are generally provided in the class to access or modify the state more abstractly. In practice sometimes methods (so-called "getters" and "setters") are provided to access the values indirectly, but, although not necessarily a violation of abstract encapsulation, they are often considered a sign-post of potentially poor object-oriented programming (OOP) design practice -
Function prototype(函式原型)/ Function interface(函式介面):a declaration of a function that specifies the function’s name and type signature(型別簽名)(arity(變元個數), data types of parameters (formal argument 形式引數), and return type), but omits the function body
-
Include directive: In C and C++, the
#includepreprocessor directive causes the compiler to replace that line with the entire text of the contents of the named source file (if included in quotes:"") or named header (if included in angle brackets:<>), note that a header doesn't need to be a source file
Headers need not have names corresponding to files: in C++ standard headers are typically identified with words, like "vector", hence#include <vector>while in C standard headers have identifiers in the form of filenames with a ".h" extension, as in#include <stdio.h>. A "source file" can be any file, with a name of any form, but is most commonly named with a ".h" extension and called a "header file" (sometimes ".hpp" or ".hh" to distinguish C++ headers), though files with .c, .cc, and .cpp extensions may also be include.
In practice, what is usually done is that the angle-brackets form searches for source files in a standard system directory (or set of directories), and then searches for source files in local or project-specific paths (specified on the command line, in an environment variable, or in a Makefile or other build file), while the form with quotes does not search in a standard system directory, only searching in local or project-specific paths. In case there is no clash, the angle-brackets form can also be used to specify project-specific includes, but this is considered poor form. The fact that headers need not correspond to files is primarily an implementation technicality, and used to omit the .h extension in including C++ standard headers; in common use "header" means "header file". -
size_t: size_t的取值range是目標平臺下最大可能的陣列尺寸,能增加代碼可移植性,
-
int補碼表示:按位取反加一
-
物件:this指標
//此處的this指標可省略(物件作為整體訪問時要顯式地使用this指標,即*this) void create(int n, int d) {this->num = n; this->den = d; this->ReductFraction();} -
構造:
-
建構式的名字與類相同,是類的成員函式
-
定義物件時自動呼叫,不能指定回傳型別
-
建構式可以(共用函式名),定義物件時根據初值選擇相應的建構式
-
定義時必須給出實際引數表
類名 物件名(實際引數表);
除非有一個建構式沒有引數才可用類名 物件名(); -
不帶引數的建構式稱為默認建構式,一個類通常只有一個默認建構式
-
編譯器自動生成的建構式沒有引數,函式體為空,此時生成物件的所有資料成員初值為隨機值
-
寫了建構式就一定要有對應的實參,因為編譯器不再生成空的建構式了
-
指定默認值可以解決不想寫默認建構式的麻煩
-
初始化串列:
DoubleArray::DoubleArray(int lh, int rh):low(lh), high(rh) { storage = new double [high - low + 1]; } -
初始化串列的好處:使資料成員的初始化和賦初值同步進行,提高了函式的效率, 否則系統會先執行每個資料成員對應類的默認建構式,再執行整個類的默認建構式
-
必須用初始化串列的場合:
- 資料成員是某個類的物件,不能直接用賦值陳述句賦初值
- 類包含一個常量的資料成員
-
初始化次序是按照定義時的次序來的
-
拷貝建構式:同型別變數初始化
//雖然我們可以隨便寫,但是其本意是構造一個一模一樣的物件 Rational(const Rational &obj) //不能寫成值傳遞,否則會自我無限呼叫而不終止 {num = 2 * obj.num; den = obj.den;} Rational r3 = r1; -
默認拷貝建構式的缺點:如果有指標的話會指向同一塊空間
-
呼叫拷貝建構式
//物件定義時 DoubleArray array2(array1); DoubleArray array2 = array1; //函式呼叫時:值傳遞 //函式回傳時:回傳值等于一個臨時物件
-
-
析構:
~DoubleArray(){if (storage) delete [] storage;}無引數無回傳值 -
生成默認建構式:
CreateAndDestroy() = default; -
阻止拷貝:
BoubleArray(const DoubleArray &) = delete; -
委托建構式
-
初始化串列
-
類內初始化
-
const與類
- 常量函式成員
const int size; //只能在建構式的初始化串列中完成 - 常量物件
const Rational r1(1, 3); //只能用建構式初始化不能使用賦值陳述句 - 常量成員函式
不改變資料成員的函式都必須宣告為const,在函式頭后加一個保留字const
常量物件只能呼叫常量成員函式
- 常量函式成員
-
靜態成員
-
靜態資料成員
static double rate;
類的靜態成員函式擁有一塊單獨的存盤區,只在類的范圍內有效,可以是公有或私有
定義在類的實作檔案中
double SavingAccount::rate = 0.05; //類的空間只有在定義時分配,必須單獨在類的實作檔案中定義,才能分配空間
可以通過類名直接呼叫,如:類名::靜態資料成員名;或和普通成員一樣呼叫 -
靜態成員函式
為類服務,而不是類的物件
可以寫在類定義中static void SetRate(double newRate);也可以寫在類定義外面,不加static
可以通過類名直接呼叫,如:類名::靜態成員函式(實際引數表);或和普通成員函式一樣呼叫
沒有有隱含的this指標,只能訪問靜態成員 -
靜態常量成員
整個類共享的常量
一般而言,類的資料成員不能再類定義時初始化:- 普通的資料成員在物件定義時由解構式初始化
- 靜態資料成員在靜態資料成員定義時初始化
- 例外:靜態常量資料成員必須在類定義時初始化
-
-
友元:
最好把友元函式的申明放在類定義最前面或最后面,并且在前面不添加任何訪問控制說明- 友元(全域)函式
friend void f();
定義可以寫在類定義里 - 友元成員函式
friend int B::func(double); - 友元類
friend class B; //class B 中所有成員函式都可以直接訪問該類私有成員
- 友元(全域)函式
-
運算子多載:不改變運算物件數,不改變優先級和結合性
-
多載為全域函式(作為類的友元函式)
//多載成友元函式,引數的個數,型別,回傳型別與期望的完全相同 //具有兩個運算物件其回傳一個新的物件的運算子建議多載為全域函式,如+, -, *, > //更加靈活,第一個運算物件不是this指標,可以進行自動型別轉換 friend Complex operater+(const Complex &c1, const Complex &c2) {return Complex(c1.real + c2.real, c1.imag + c2.imag);} //這里默認建構式寫在后面的public中 friend Complex operater-(const Complex &c1, const Complex &c2) {return Complex(c1.real - c2.real, c1.imag - c2.imag);} -
函式呼叫運算子多載
-
多載為成員函式
//c++規定隱含引數this是運算子的第一個引數 //必須設為公有成員函式,且不改變運算物件值的函式設為const的成員函式 //=, [], (), ->必須多載成成員函式,這樣編譯器才能檢查第一個運算物件是不是相應的類物件 //++, --, public: Rational operater+(const Rational &r1) const { Rational tmp; tmp.num = num * r1.den + r1.num * den; //這里的num和den都是隱式添加了this->的 tmp.den = den * r1.den; tmp.ReductFraction(); return tmp; } Rational operater*(const Rational &r1) const; //賦值運算子多載 //c++中賦值運算構成一個運算式,回傳值為左邊物件本身,可以是左值 //這樣就可以實作用于類似 (a=b)=c 這樣的再次對a=b進行寫操作的運算式 DoubleArray &DoubleArray::operater=(const DoubleArray &right) { if (this == &right) return *this; //防止自己復制自己,否則會把自己delete掉了 delete [] storage; //歸還空間 low = right.low; high = right.high; storage = new double[high - low + 1]; //重新申請空間 for (int i = 0; i <= high - low; ++i) //復制陣列元素 storage[i] = right.storage[i]; return *this; } //一般需要自定義復制建構式的類也需要多載賦值運算子 Rational r2 = r1; //拷貝建構式 r1 = r3; //賦值運算子多載函式 //下標運算子的多載 //回傳對應陣列元素的參考,可以是左值 double &DoubleArray::operator[](int index) { if (index < low || index > high) {cout << "下標越界"; exit(-1);} return storage[index - low]; } //回傳右值 const double operator[](int i) const; //前綴++ Rational &Rational::operator++(); //回傳的是當前物件,不會消亡 //后綴++ //為與前綴區分,接受一個額外無用的int形參,使用后綴時,編譯器自動用0作為這個引數的值 Rational Rational::operator++(int); //回傳的是一個新建的物件,函式結束后消亡,不可參考 //盡量使用前置
-
-
組合:
一個類的某個資料成員是另外一個類的物件,則該物件稱為物件成員
如果類含有物件成員,新類物件初始化時也不想用默認建構式去初始化物件成員,必須用建構式的初始化串列去初始化成員物件,
Complex(int r1 = 0, int r2 = 1, int i1 = 0, int i2 = 1):real(r1, r2), imag(i1, i2) {}
Complex(Rational r, Rational i):real(r), imag(i) {} -
繼承:
-
例外處理:
例外拋出時,throw后運算元初始化該型別的一個臨時副本,傳回呼叫該程式的程式,當前函式終止,區域變數被析構-
throw myerror(“something bad happened”);myerror是一個類,它以字串變數為引數
-
throw int(5)
int是一個內部型別,5是一個int型別的常數 -
// Class DivideByZeroException to be used in exception // handling for throwing an exception on a division by zero. class DivideByZeroException { public: //建構式 DivideByZeroException():message("attempted to divide by zero") {} const char *what() const {return message;} //回傳一個常量字符陣列(字串) private: const char *message; //記錄出現的例外情況 };
-
-
容器:
容器是保存和處理一組有某種關系的同類資料的型別,迭代器是一個抽象指標 -
堆疊:
- LIFO
- 對于n個不同的元素,給定入堆疊的順序,求有多少種出堆疊順序,
- 考察最后一個出堆疊的元素是第幾個入堆疊的元素
重要觀察:如果最后出堆疊的是a[i],則a[i]入堆疊前a[1]…a[i-1]必然已經出堆疊,這種出堆疊的順序數為f(i-1)
剩下a[i+1]…a[n]的出堆疊順序數為f(n-i)
卡特蘭數:DP復雜度O(n^2) - 從另一個角度思考(復雜度O(n))
令1表示進堆疊,0表示出堆疊,則原問題可轉化為求一個2n位、含n個1、n個0的二進制數,滿足從左往右掃描到任意一位時,經過的0數不多于1數的個數,
含n個1、n個0的2n位二進制數共有C(2n,n)個,其中不滿足條件的有C(2n,n+1)個,
原因:不滿足條件的數在某一位第一次0的個數大于1的個數,從那以后全部按位取反可以得到一個n+1個0,n-1個1的數,這個數與原來的數一一對應(為什么?我已經想清楚了) - 堆疊的鏈表實作:
用鏈表頭的指標指向堆疊頂(不能用tail,因為洗掉操作必須在堆疊頂完成) - 重要的堆疊
函式的呼叫和回傳:
在堆疊中記錄函式呼叫回傳的地址,一層層push進去
遞回呼叫堆疊有深度,不能呼叫太多次 - 堆疊的應用
DFS
括號匹配
-
佇列:
-
FIFO
-
佇列的實作:
-
隊頭位置不固定
-
含空單元的回圈佇列
-
少用一個存盤單元,當順序存盤空間的容量為maxSize時,只允許最多存盤maxSize-1個資料元素,此時判斷空的條件為 front == rear ,判斷滿的條件是front == (rear+1) % maxSize;
-
一般記錄兩個指標,指向隊首隊尾
-
-
佇列的應用
云計算
BFS
排隊系統
滑動視窗的最大值
-
-
while(cin>>a)
只要輸入的值有效,那么就執行while體內的陳述句,
(1)若流是有效的,即流未遇到錯誤,那么檢測成功,
(2)若遇到檔案結束符,或遇到一個無效的輸入時(例如本題輸入的值不是一個整數)istream物件的狀態會變為無效,條件就為假,
本文來自博客園,作者:Danny2003,轉載請注明原文鏈接:https://www.cnblogs.com/Danny2003/p/15784406.html
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/415197.html
標籤:C++
上一篇:嘗試通過UDP執行TLS:有關大訊息的wsarecv錯誤
下一篇:分巧克力(刷題賽)
