一、C語言和c++的函數重載(同名不同參),相互調用,對象(參數)傳遞與引用。
1. 類外定義函數,可用“類名::函數名”進行編寫,在類內需要聲明該函數
Class stu{
//成員
//屬性
Private://沒有private就默認public
Int age;
Char* name;
//方法engji
Public:
Void init();
}
Void stu::init(){ //stu::表明該函數為類內部方法 }
int main(){
stu s;
return 0;
}
2. 結構體成員按對齊分配大小,取決於最大的
例如:
Struct stu{
double a; //8位
int i; //加8位(原本只有4位)等於16位
char c; //由於Int后4位沒有占用,char直接占用,16不改變
char c2; // 同上
}
sizeof(stu) = 16
3. 對象的創建,this指針指向調用的對象,對象的引用
java用new創建對象時的內存分配。
Stu s; //java中使用這語句時,定義標識符s,沒有實際空間,只有new才分配空間創建對象
Stu stu; //在棧中申請空間,不能有括號,在程序結束后自動執行析構函數銷毀對象
Stu stu1 = new Stu(); //在堆中申請空間
Stu *pstu = &stu1; //new創建類對象需要指針接收,一處初始化,多處使用
Stu *stu2 = new Stu();
delete pstu; //刪除對象new創建類對象使用完需delete銷毀
4. 類的定義與封裝
class Stu{
int age;
char* name;
public: //公有的方法
void init(int age, char* name){} //初始化方法
}
void Stu::init(int age, char* name){ //Stu::指明了該函數為類內部方法
this->age = age;
this->name = name;
}
int main(int argc, char* argv[]){
Stu stu1;
stu1.init(19, "ye");
}
5.類方法(函數)構造,無返回值也無void,可以無參數;constructor, destructor
class Stu{
int age;
char* name;
public: //公有的
//實例函數,但不是構造函數
void init(int age, char* name){} //初始化對象屬性值的函數
/*構造函數必須同時滿足下面三個條件:
1、函數名和類名相同
2、在函數名的前面沒有返回值類型的聲明,void也沒有
3、在函數中不能使用return語句返回一個值
4.構造函數在創建對象時自動執行,一般不能顯式地直接調用*/
Stu();//可以無參數,用來初始化對象(默認被構造)
Stu(int age); //函數重載
Stu(int age=0, char* name);//默認參數
Stu(int* num);
}
析構函數:對象消亡時,自動被調用,用來釋放對象(或構造函數請求的)占用的空間。
特點:
(1) 名字與類名相同
(2) 在前面需要加上"~"
(3) 無參數,無返回值
(4) 一個類最多只有一個析構函數
(5) 不顯示定義析構函數會調用缺省析構函數
class Test{
int id;
public:
Test(int i){
id = i;
}
~Test(){
cout<<"ID: "<<id<<" destruction function is invoked!"<<endl;
};
};
int main(){
Test t0(0); //棧中分配
Test t1[3]{1,1,1}; //棧中分配,數組型對象
Test *t2 = new Test(2); //堆中分配
delete t2;
Test *t3 = new Test[3]{3,3,3};//堆中分配
delete []t3;
cout<<"------End of Main-------"<<endl;
return 0;
}
copy construction,拷貝構造函數是一種特殊的構造函數,函數的名稱必須和類名稱一致,它必須的一個參數是本類型的一個引用變量。
在某些狀況下,類內成員變量需要動態開辟堆內存,如果實行位拷貝,也就是把對象里的值完全復制給另一個對象,如A=B。這時,如果B中有一個成員變量指針已經申請了內存,那A中的那個成員變量也指向同一塊內存。這就出現了問題:當B把內存釋放了(如:析構),這時A內的指針就是野指針了,出現運行錯誤。
深拷貝和淺拷貝可以簡單理解為:如果一個類擁有資源,當這個類的對象發生復制過程的時候,資源重新分配,這個過程就是深拷貝,反之,沒有重新分配資源,就是淺拷貝。
所謂淺拷貝,就是說編譯器提供的默認的拷貝構造函數和賦值運算符重載函數,僅僅是將對象a中各個數據成員的值拷貝給對象b中對應的數據成員(這里假設a、b為同一個類的兩個對象,且用a拷貝出b或用a來給b賦值),而不做其它任何事。
當用一個已初始化過了的自定義類類型對象去初始化另一個新構造的對象的時候,拷貝構造函數就會被自動調用。也就是說,當類的對象需要拷貝時,拷貝構造函數將會被調用。以下情況都會調用拷貝構造函數:
<![if !supportLists]>l <![endif]>一個對象以值傳遞的方式傳入函數體
<![if !supportLists]>l <![endif]>一個對象以值傳遞的方式從函數返回
<![if !supportLists]>l <![endif]>一個對象需要通過另外一個對象進行初始化。
<![if !supportLists]>l <![endif]>
1 #include<iostream> 2 3 using namespace std; 4 5 class CExample 6 7 { 8 9 private: 10 11 int a; 12 13 public: 14 15 //構造函數 16 17 CExample(int b) 18 19 { 20 21 a=b; 22 23 printf("constructor is called\n"); 24 25 } 26 27 //自定義的拷貝構造函數,深拷貝 28 29 CExample(const CExample & c) 30 31 { 32 33 a=c.a; 34 35 printf("copy constructor is called\n"); 36 37 } 38 39 //析構函數 40 41 ~CExample() 42 43 { 44 45 cout<<"destructor is called\n"; 46 47 } 48 49 void Show() 50 51 { 52 53 cout<<a<<endl; 54 55 } 56 57 }; 58 59 60 61 int main() 62 63 { 64 65 CExample A(100); 66 67 CExample B=A; 68 69 B.Show(); 70 71 return 0; 72 73 } 74 75
二、變量與常量基本使用
6.static local variable使用
1 /* static local variable 2 3 靜態局部變量,不會分配在傳統的棧區,不會被釋放,效果相當於全局變量,但不會被函數外的所訪問 4 5 僅在聲明它的文件中可見,同一工程的其它文件中不可見,可解決不同文件的變量/函數重名問題; 6 7 */ 8 9 int global; //全局變量在程序開始時(main之前)被創建,結束時釋放 10 11 12 13 void fun(){ 14 15 static int i = 0; //在第一次執行時被創建,程序完全結束后才釋放,並不是函數退出釋放當且僅當第一次執行才初始化,之后的值相當於保留上次結果 16 17 int j; 18 19 int k; 20 21 cout << i++ << endl; 22 23 cout << &j << &k << &i << endl; //地址與j,k不在同一區域 24 25 } 26 27 28 29 class Test{ 30 31 int id; 32 33 static int j; //類中對象所共有的 34 35 public: 36 37 Test(int i){ 38 39 id = i; 40 41 } 42 43 ~Test(){ 44 45 cout<<"ID: "<<id<<" destruction function is invoked!"<<endl; 46 47 }; 48 49 static void a_func(); //靜態函數,可以直接通過類調用函數 50 51 }; 52 53 54 55 /*類似全局變量的方式對類內的局部變量定義,共享使用,相當於對所有對象初始化,無論這個類的對象定義了多少個,靜態數據成員在程序中也只有一份拷貝,由該類型的所有對象共享訪問。 56 57 */ 58 59 //即靜態數據成員是該類的所有對象所共有的 60 61 int Test::j = 10; 62 63 64 65 int main(){ 66 67 for(int i = 0; i <= 10; i++) 68 69 fun(); //i輸出1,2,3,4…… 70 71 Test::a_func(); //static型可直接調用,而不需要創建對象 72 73 return 0; 74 75 } 76 77
全局變量與命名空間namespace
所謂namespace,是指標識符的各種可見范圍。C++標准程序庫中的所有標識符都被定義於一個名為std的namespace中。
在類中:加上類名對函數和方法進行限定

1 class A //聲明A類 2 3 { public: 4 5 void funl();//聲明A類中的funl函數 6 7 private: 8 9 int i; }; 10 11 void A::funl() //定義A類中的funl函數 12 13 {…………}
自定義名:

namespace name1 // 指定命名中間 { int a; double b; }
當定義全局變量的時候如果變量名與名空間std內的變量名沖突,例如定義全局變量count, max等編譯器會報“變量不明確的錯誤”,通過用::符號來區別

1 // C2872.cpp 2 // compile with: cl /EHsc C2872.cpp 3 namespace A { 4 int i; 5 } 6 7 using namespace A; 8 int i; 9 int main() { 10 ::i++; // ok, uses i from global namespace 11 A::i++; // ok, uses i from namespace A 12 i++; // C2872 ambiguous: ::i or A::i? 13 // To fix this issue, use the fully qualified name 14 // for the intended variable. 15 }
7.const 常量的使用
1 /* const 2 3 常量 4 5 */ 6 7 #include<iostream> 8 9 #include<cstring> 10 11 using namespace std; //命名空間,類作用域 12 13 14 15 const int i = 0; //常量不允許賦值,所以必須初始化,其作用域不會被改變 16 17 const int v[] = {1,2,3}; 18 19 20 21 void f1(const int i){ 22 23 i++; // Illegal compile 參數傳遞const值不能被改變 24 25 } 26 27 28 29 const int* const w(){ //返回值要求這個指針以及這個指針所指向的對象均為常量 30 31 static int i; //靜態的局部變量,有效 32 33 return &i; 34 35 } 36 37 38 39 const int g(){}; // 返回const值,約定了函數框架里的原變量不會被修改 40 41 42 43 int main(){ 44 45 strcpy(s1, s2); // char * strcpy(char *s1, const char *s2); 46 47 //含有const為輸入,無則為輸出,且 s2不會被修改,稱作指針參數 48 49 return 0; 50 51 } 52 53 54 55 /* const常量,保證只有一個對象,在全局作用域里聲明的const變量是定義該對象的文件的局部變量,此變量只存在於那個文件中,不能被其它文件訪問。 56 57 */ 58 59 class Schedule() { 60 61 Schedule(); 62 63 static Schedule* self; 64 65 public: 66 67 static Schedule* get_instance(); 68 69 } 70 71 72 73 Schedule* Schedule::self = NULL; 74 75 76 77 int main(){ 78 79 Schedule* s = Schedule::get_instance(); 80 81 return 0; 82 83 } 84 85 86 87 /* const常量在類中使用,const引用是指向const對象的引用, 88 89 */
1 #include<stdlib.h> 2 3 4 5 class Fred() { 6 7 const int size; // 不能直接初始化,需要在構造函數里 8 9 public: 10 11 Fred(int sz); 12 13 void print(); 14 15 } 16 17 18 19 Fred::Fred(int sz) : size(sz) {} //在構造函數里,const必定已經初始化了 20 21 void Fred::print() { 22 23 cout << size << endl; 24 25 } 26 27 28 29 int main(){ 30 31 Fred a(1), b(2), c(3); 32 33 a.print(), b.print(), c.print(); 34 35 return 0; 36 37 } 38 39
8.運算符重載
博客:大整數運算符重載:https://www.cnblogs.com/frankying/p/8544337.html
賦值運算符重載函數不能被繼承
1 #include<cstdio> 2 3 #include<cstring> 4 5 #include<vector> 6 7 #include<algorithm> 8 9 #include<iostream> 10 11 using namespace std; 12 13 14 15 struct BigInteger { 16 17 static const int BASE = 100000000; 18 19 static const int WIDTH = 8; 20 21 vector<int> s; 22 23 24 25 BigInteger(long long num = 0) { *this = num; } // 構造函數 26 27 BigInteger operator = (long long num) { // 賦值運算符 28 29 s.clear(); 30 31 do { 32 33 s.push_back(num % BASE); 34 35 num /= BASE; 36 37 } while (num > 0); 38 39 return *this; 40 41 } 42 43 BigInteger operator = (const string& str) { // 賦值運算符 44 45 s.clear(); 46 47 int x, len = (str.length() - 1) / WIDTH + 1; 48 49 for (int i = 0; i < len; i++) { 50 51 int end = str.length() - i*WIDTH; 52 53 int start = max(0, end - WIDTH); 54 55 sscanf(str.substr(start, end-start).c_str(), "%d", &x); 56 57 s.push_back(x); 58 59 } 60 61 return *this; 62 63 } 64 65 BigInteger operator + (const BigInteger& b) const { 66 67 BigInteger c; 68 69 c.s.clear(); 70 71 for (int i = 0, g = 0; ; i++) { 72 73 if (g == 0 && i >= s.size() && i >= b.s.size()) break; 74 75 int x = g; 76 77 if (i < s.size()) x += s[i]; 78 79 if (i < b.s.size()) x += b.s[i]; 80 81 c.s.push_back(x % BASE); 82 83 g = x / BASE; 84 85 } 86 87 return c; 88 89 } 90 91 BigInteger operator - (const BigInteger& b) const { 92 93 BigInteger c; 94 95 c.s.clear(); 96 97 for (int i = 0, g = 0; ; i++) { 98 99 if (g == 0 && i >= s.size() && i >= b.s.size()) break; 100 101 int x = g; 102 103 if (i < s.size()) x += s[i]; 104 105 if (i < b.s.size()) { 106 107 if (s[i] < b.s[i]) { 108 109 x += BASE; 110 111 g = -1; 112 113 } 114 115 x -= b.s[i]; 116 117 } 118 119 c.s.push_back(x % BASE); 120 121 } 122 123 return c; 124 125 } 126 127 BigInteger operator += (const BigInteger& b) { 128 129 *this = *this + b; 130 131 return *this; 132 133 } 134 135 136 137 bool operator < (const BigInteger& b) const { 138 139 if (s.size() != b.s.size()) 140 141 return s.size() < b.s.size(); 142 143 for (int i = s.size()-1; i >= 0; i--) //從低位開始比 144 145 if (s[i] != b.s[i]) 146 147 return s[i] < b.s[i]; 148 149 return false; // 相等 150 151 } 152 153 bool operator > (const BigInteger& b) const { return b < *this; } 154 155 bool operator <= (const BigInteger& b) const { return !(b < *this); } 156 157 bool operator >= (const BigInteger& b) const { return !(*this < b); } 158 159 bool operator != (const BigInteger& b) const { return (*this < b || b < *this); } 160 161 bool operator == (const BigInteger& b) const { return (!(*this < b) && !(b < *this)); } 162 163 }; 164 165 166 167 ostream& operator << (ostream &out, const BigInteger& x) { 168 169 out << x.s.back(); 170 171 for (int i = x.s.size()-2; i >= 0; i--) { 172 173 char buf[20]; 174 175 sprintf(buf, "%08d", x.s[i]); 176 177 for (int j = 0; j < strlen(buf); j++) 178 179 out << buf[j]; 180 181 } 182 183 return out; 184 185 } 186 187 188 189 istream& operator >> (istream &in, BigInteger& x) { 190 191 string s; 192 193 if (!(in >> s)) return in; 194 195 x = s; 196 197 return in; 198 199 } 200 201 202 203 #include<set> 204 205 #include<map> 206 207 set<BigInteger> s; 208 209 map<BigInteger, int> m; 210 211 212 213 int main() { 214 215 BigInteger y; 216 217 BigInteger x = y; 218 219 BigInteger z = 123; 220 221 222 223 BigInteger a, b; 224 225 cin >> a >> b; 226 227 cout << a - b << "\n"; 228 229 //cout << BigInteger::BASE << "\n"; 230 231 return 0; 232 233 }
9.malloc, free, new delete
/* new delete使用
*/
#include<stdlib.h>
int main(){
new <=> malloc + 構造construction; //返回是對象類型的指針,(自由存儲區)動態內存分配,malloc從堆中分配,返回值需要強制類型轉換
delete <=> 析構deconstruct + free;
/*使用new操作符來分配對象內存時會經歷三個步驟:
第一步:調用operator new函數(對於數組是operatornew[])
*分配一塊足夠大的,原始的,未命名的內存空間以便存儲特定類型的對象。
第二步:編譯器運行相應的構造函數以構造對象,並為其傳入初值。
第三步:對象構造完成后,返回一個指向該對象的指針。
使用delete操作符來釋放對象內存時會經歷兩個步驟:
第一步:調用對象的析構函數。
第二步:編譯器調用operator delete(或operator delete[])函數釋放內存空間。
總之來說,new/delete會調用對象的構造函數/析構函數以完成對象的構造/析構。而mall *oc則不會。*/
return 0;
}
10.inline函數
1 /* 在線化函數,給編譯器一個提示,要求它試着把所有對fac()的調用在線化 2 3 一個inline函數必須在需要用它的每個編譯文件里做完全一樣的定義 4 5 為了避免錯誤,一般把const(默認具有內部連接)和inline放在頭文件里定義 6 7 */ 8 9 inline int fac(int n){ 10 11 return (n<2)?1:n*fac(n-1); 12 13 } 14 15 16 17 // extern關鍵字,在一個程序里,一個對象只能定義一次,但可以有多個聲明(類型必須完全一樣),file2.c文件里的extern指明了x只是一個聲明,而在file1.c中被定義了 18 19 // file1.c: 20 21 int x; 22 23 int f(){} 24 25 26 27 // file2.c: 28 29 extern int x; 30 31 int f(); 32 33 void g(){ x= f(); } 34 35
11.default parameter默認參數
通常情況下,函數在調用時,形參從實參那里取得值。對於多次調用用一函數同一實參時,C++給出了更簡單的處理辦法。給形參以默認值,這樣就不用從實參那里取值了。
1 float volume(float length, float weight = 4, float high = 5) 2 3 { 4 5 return length*weight*high; 6 7 } 8 9 int main() 10 11 { 12 13 float v = volume(10); 14 15 float v1 = volume(10,20); 16 17 float v2 = volume(10,20,30); 18 19 cout<<v<<endl; 20 21 cout<<v1<<endl; 22 23 cout<<v2<<endl; 24 25 return 0;
規則
1.默認的順序,是從右向左,不能跳躍。
2.函數聲明和定義一體時,默認認參數在定義(聲明)處。聲明在前,定義在后,默認參數在聲明處。
3.一個函數,不能既作重載,又作默認參數的函數。當你少寫一個參數時,系統無法確認是重載還是默認參數
12. 宏定義與頭文件定義
(1)簡單的宏定義:
#define <宏名> <字符串>
例: #define PI 3.1415926
(2) 帶參數的宏定義
#define <宏名> (<參數表>) <宏體>
例: #define A(x) x
1)文件包含
可以把源程序中的#include 擴展為文件正文,即把包含的.h文件找到並展開到#include 所在處。
(2)條件編譯
預處理器根據#if和#ifdef等編譯命令及其后的條件,將源程序中的某部分包含進來或排除在外,通常把排除在外的語句轉換成空行。
(3)宏展開
預處理器將源程序文件中出現的對宏的引用展開成相應的宏 定義,即本文所說的#define的功能,由預處理器來完成。
經過預處理器處理的源程序與之前的源程序有所有不同,在這個階段所進行的工作只是純粹的替換與展開,沒有任何計算功能,所以在學習#define命令時只要能真正理解這一點,這樣才不會對此命令引起誤解並誤用。
define中的三個特殊符號:#,##,#@
#define Conn(x,y) x##y
#define ToChar(x) #@x
#define ToString(x) #x
(1)x##y表示什么?表示x連接y,舉例說:
int n = Conn(123,456); /* 結果就是n=123456;*/
char* str = Conn("asdf", "adf"); /*結果就是 str = "asdfadf";*/
(2)再來看#@x,其實就是給x加上單引號,結果返回是一個const char。舉例說:
char a = ToChar(1);結果就是a='1';
做個越界試驗char a = ToChar(123);結果就錯了;
但是如果你的參數超過四個字符,編譯器就給給你報錯了!
error C2015: too many characters in constant :P
(3)最后看看#x,估計你也明白了,他是給x加雙引號
char* str = ToString(123132);就成了str="123132";
頭文件中用到的 #ifndef/#define/#endif 來防止該頭文件被重復引用
#ifndef __struct_h__ // if not define #define __struct_h__ // define class Struct{ }; void func(); #endif
三、繼承
1 #include<stdlib.h> 2 3 public class B { 4 5 int i; 6 7 char *b; 8 9 public: 10 11 B(); 12 13 void fun(); 14 15 } 16 17 18 19 class T : public B { // T繼承B,子類自動擁有父類的屬性和行為 20 21 22 23 void T::fun(){ // 子類重載父類 24 25 B::fun(); // 繼承父類的行為 26 27 /* 28 29 * 子類特性行為 30 31 */ 32 33 } 34 35 }
四、多態
將派生類對象賦值給基類對象、將派生類指針賦值給基類指針、將派生類引用賦值給基類引用,向上轉型指針或引用,而非傳值。
抽象類可以有成員變量,接口不能有。
1. 虛基類
2.純虛函數與抽象類:
1 #include<stdlib.h> 2 /* 我們把包含純虛函數的類稱之為抽象類 3 對於抽象類來說,C++是不允許它去實例化對象的。也就是說,抽象類無法實例化對象。 4 抽象類只用來繼承 5 */ 6 class T : public B { // T繼承B,子類自動擁有父類的屬性和行為 7 public: 8 virtual void fun2(); // virtual func,函數體可以有內容{。。。} 9 10 virtual void fun() = 0; // 純虛函數 11 12 } 13 14 int main(){ 15 16 return 0; 17 }