一, 輸入輸出函數 標准輸出函數cout,其語句的一般格式為 Cout<<表達式; Cout<<表達式<<endl;(endl為換行符相當於‘\n’) Cout<<表達式1<<表達式2<<……<<表達式n; 同理標准輸入函數cin其語法格式為 Cin>>表達式1>>表達式2>>表達式n>>endl; C++的標准輸入輸出於C語言來說比較方便,不需要輸入輸出格式說明 只要用到標准輸入輸出函數就必須調用庫函數輸入輸出流庫<iostream>和域名空間using namespace std. 二、字符串變量 C++中多了一種字符串類型的變量(string),定義格式為string s;可以直接進行字符串變量的加比較。例如,string s1=“你好”;string s2=“世界”;s1+s2=“你好世界”,表達式s1>s2如果正確返回1,否則返回0.使用string變量同樣需要調用流庫<iostream>和域名空間using namespace std. 三、引用 Int &b=a;這裡就是一個引用,相當於給變量a引用一個新的名字b,b不會再內存開辟空間,這個時候a和b完全等價,指同一塊內存變量,操作a就相當於操作b。引用主要用於傳遞函數的參數和返回值,例如: Void change1(int &r) { r=100; } Void change2(int *p) { *p=1000; } void main() { Int a=1; change1(a); cout<<a<<endl; change2(&a); cout<<a<<endl; } 結果輸出100,1000.可見引用傳遞和指針傳遞都可以改變參數的值。 引用的使用需要注意以下幾點: 1. 引用必須在聲明時立即初始化,不允許空引用; 2. 引用一旦初始化,就不能再更改,不能再引用其他數據; 3. 引用和被引用的變量實際上代表同一個內存的數據。 四、類 類是具有相同屬性和行為的一組對象的集合,主要包括屬性和行為兩個部分。類類型是C++中最主要的數據結構類型。 聲明一個類類型 class student{ public;//訪問權限修飾符 int num;//聲明成員變量 char sex; char name[20];//聲明成員函數 void display(); } Void student::display() { }//成員函數聲明後必須要定義,可以在成員函數聲明時就定義,也可以在類類型聲明外面定義,在外面定義格式為 函數返回值+類類型+雙冒號+函數名+函數參數 定義一個對象: 類型名 對象名(構造函數參數)//構造函數的參數默認為空 對象只保存成員變量,存儲在棧中,函數在代碼區。訪問對象的成員變量的方法: 對象.成員 對象.成員函數(實參列表) 對象的指針->成員變量 對象的指針->成員函數名(實參列表) 對象的引用.成員變量 對象的引用.成員函數(實參列表) 類名::靜態成員變量名 類名::靜態成員函數名(實參列表) 五.成員函數重載 成員函數的名字一樣但是成員函數參數不同包括參數的類型、個數、順序不一樣的現象叫做成員函數重載。成員函數的重載與函數的返回值無關。判斷兩個成員函數是否重載的最總取決於將同樣的參數放在函數名一樣的成員函數中看是否出現歧義,若沒有歧義則是同一函數,若有歧義則是成員函數重載。 六、構造函數 構造函數是一種特殊的成員函數: 1, 構造函數的名字必須要與類名相同 2, 它不具有任何類型,不返回任何值 3, 構造函數的功能是由用戶定義的 4, 構造函數可以重載 5, 構造函數在生成對象時就執行 構造函數的聲明格式為: 類名(); 類名(形參列表) 關於構造函數一些總結 1. 如果一個類中沒有自定義構造函數,那系統會默認生成一個public修飾的無參數空實現的構造函數 2. 當自定義了構造函數,則默認的無參數的構造函數就不存在了 3. 創建對象的時候會調用類中其中的一個構造函數 4. 構造函數可以重載,創建對象時具體調用哪一個構造函數由傳參數來確定 5. 構造函數的作用主要用於初始化正在創建的對象 七、成員變量的初始化 采用構造函數初始化成員變量 例如 Stu:Stu(int age,string name){ this->age=age; this->name=name;//構造函數中的this指正在穿件的對象的地址 } 采用初始化表為對象的成員變量賦值 例如 Stu::Stu(int xage,string xname):age(xage),name(xname){ } 八、析構函數 析構函數名字是類名的前面加一個“~”符號。沒有參數,並且不能夠重載。 當對象的生命期結束時,會自動執行析構函數。 析構函數一般用於完成“清理”的工作,如果用戶沒有自定義析構函數,C++編譯系統會自動生成一個析構函數,但它什麼操作都不進行。想讓析構函數完成任何工作時,都必須要自定義析構函數。 構造函數和析構函數按照棧中先進後出的順序 九、對象數組 數組不僅可以由簡單的變量組成,也可以由對象組成(對象數組的每一個元素都是同類的對象) 1. 調用可以不傳參數的構造函數創建對象數組 Student stud[50]; 2. 調用可以只傳一個參數的構造函數有兩種方法創建對象數組 Student stu[3]={60,70,80}; 或者 Student stu[3]={Student(60),Student(70),Student(80)}; 3. 調用可以傳多個(這裡舉例3個)參數的構造函數創建對象數組 Student stu[3]={Student(1001,1,10),Student(1002,2,11),Student(1003,3,12)}; 不能夠直接Student stu[3]={(1001,1,10),(1002,2,11),(1003,3,12)};這樣的話只是把每個括號裡最後一個參數傳給了構造函數參數列表中的第一個參數,其他的參數並沒有傳到 十、指針 指向對象成員變量的指針 定義 數據類型名 *指針變量名; 成員變量值的形式:&成員變量指針 指向成員函數的指針 定義指向成員函數的指針變量的形式 1. 無參數成員函數 成員函數返回類型 (類名::指針變量名)(); 2. 有參數成員函數 成員函數返回類型 (類名::指針變量名)(參數列表); 成員函數指針變量值的形式: &類名::成員函數名; 成員函數指針變量使用形式: (對象.*指針變量名)(實參); 指向對象的常指針和指向常對象的指針 類名 *const 指針變量名;(常指針) 禁止改變指針,指針是不變的 const 類名 *指針變量名;(常對象指針) 禁止改變所指向的對象,對象時不變的 例如: void f1() { A a1;A a2; A * const p = &a1; //p = &a2;//錯,常指針不可改變 p->x = 23; } void f2() { A a1;A a2; const A *p; p = &a1;//指針可以改變 //p->x = 23;錯,常對象指針指向的對象中的內容不可以改變 p = &a2; } void f3() { A a1;A a2; const A * const p = &a1;//常對象常指針,指針和對象均不可改變 //p = &a2; 錯 //p->x = 34; 錯 } 十一、常成員變量和常成員函數以及常對象 常成員變量時用關鍵字const來修飾聲明的成員變量,const位於變量前或者類型前都一樣。例如 class Time{ public: const int hour; int const minute; } 1. 常成員變量只能通過構造函數的初始化表對常成員變量進行初始化; 例如. class A { public: //常成員變量 const int x; const int y; int z; A(int x, int y); A(int x, int y, int z); }; A::A(int x, int y): x(x),y(y) { } A::A(int x, int y, int z): x(x),y(y) { this->z = z; } 2. 常成員變量所在類中所有的構造函數都必須通過初始化表對常成員進行初始化; 3. 常成員變量可以向普通的成員變量一樣進行訪問,但是其值是不能被修改的。 4. 必須在構造函數初始化,不然為隨機數沒有意義 常成員函數:用const修飾的成員函數,const位於函數括號之後,定義和聲明的地方都要加上const。 例如 class Time{ public: void show()const; } 常成員函數可以用this訪問成員變量,但是不能修改this訪問的成員變量。(包括常成員變量和非常成員變量) 常成員函數體重用this訪問成員函數時,只能訪問常成員函數(常成員函數只能訪問常成員函數) 例如 class A { public: int x; //常成員函數 void m1()const; void m2()const; void m3(); void m4(); }; void A::m1()const { cout<<this->x<<endl; //常成員函數不能修飾成員變量 //this->x = 234;//錯 this->m2(); //常成員函數只能訪問成常函數,但是不能訪問普通的成員函數 //任函數都可以訪問常成員函數 //this->m3();//錯 } void A::m2()const { this->m1(); } void A::m3() { this->x = 234; } void A::m4() { this->m1(); } 常對象 定義常對象形式為 類名 const 對象名(實參列表);或者const 類名 對象名(實參列表); 例: class Time{ public: Time(); Time(int x); …… } const Time time1; Time const time2; const Time time3(100); Time const time4(200); 常對象特點: 1. 常對象中所有的成員變量的值都不能夠被修改; 2. 常對象訪問函數時只能訪問常成員函數; 例: class A { public: int x; int y; A(); void m1()const; void m2(); }; A::A():x(10),y(23) { }//初始化表初始化成員變量 void A::m1()const { cout<<"A::m1();"<<endl; } void A::m2() { cout<<"A::m2();"<<endl; } void f2() { const A a;//常對象 cout<<a.x<<endl;//10 cout<<a.y<<endl;//23 //常對象中成員變量的值不能修改,只能在構 //構函數中用初始化表初始化 //a.x = 23;//錯 a.m1(); //a.m2();錯 //常對象只能訪問常成員函數 } int main() { f2(); return 0;} 十二、對象動態創建與銷毀 new運算符可以動態創建對象,創建對象後會返回對象的地址。如果內存不足導致無法成功創建對象,則會返回0指針值。 動態創建的對象要用“delete 對象指針”才能銷毀對象。 BOX *pt1; pt=new BOX;//創建對象 delete pt1;//銷毀對象 如果是用malloca和free動態創建和釋放對象的話不會調用構造函數和析構函數,所以一般在C++中是采用new和delete來動態創建和銷毀對象的。 十三、對象的復制與賦值 對象賦值方法: 類名 對象1(對象2); 對象賦值的方法: 對象名1=對象名2; 類名 對象名1=對象名2; 例: //賦值 void f1() { Stu stu1,stu2; stu1.age = 20; stu1.no = 100; stu2 = stu1; stu1.show("stu1"); stu2.show("stu2"); Stu stu3 = stu1; stu3.show("stu3"); } //復制 void f2() { Stu stu1; stu1.age = 20; stu1.no = 100; Stu stu2(stu1);//本質是調用了類的復制構造函數 stu2.show("stu2"); } 1.如果自定義了非復制構造函數,類中有一個默認的復制構造函數 2.如果沒有自定義構造函數,類中默認有兩個構造函數,一個無參的, 另一個是復制構造函數 3.如果是自定義了復制構造函數,類中就一個默認的構造函數都沒有了 4.復制構造函數就是:寫一個參數為 "類的引用" 的構函數,與函數體無關 十三、靜態成員(static) 靜態成員變量 如果想在同類的多個對象之間實現數據共享,可以用靜態成員變量,即用static修飾的成員變量,例如static int a; 靜態成員變量在項目剛開始運行的時候就分配 ,在項目運行結束以後才銷毀。 靜態成員變量被它所屬類創建的所有對象所共享。 靜態成員變量必須在類體外初始化。建議放在main函數所在的文件中,格式為: 成員變量數據類型 類名::靜態成員變量=初值 例如 int Stu::a=20; 訪問靜態成員變量有兩種方式: 1. 與普通成員變量一樣,可以通過對象、對象指針或對象引用來訪問 2. 用靜態成員變量所屬類的類名加雙冒號來訪問,這種方式只能夠訪問靜態成員變量,即 類名::靜態成員變量 class Stu{ public: static int a; } int Stu::a=10;//類名雙冒號訪問變量 void f1() { cout<<"Stu::a="<<Stu::a<<endl;//10 Stu stu1;// b, c Stu stu2;// b, c cout<<"stu1.a="<<stu1.a<<endl;//10 cout<<"stu2.a="<<stu2.a<<endl;//10 靜態成員變量為同類對象所共用 stu1.a = 1000;//普通成員變量一樣的方式訪問別且修改靜態成員變量 cout<<"======================"<<endl; cout<<"Stu::a="<<Stu::a<<endl;//100 cout<<"stu1.a="<<stu1.a<<endl;//100 cout<<"stu2.a="<<stu2.a<<endl;}//100 靜態成員函數 靜態成員函數就是static修飾的成員函數 class Box{ static int volume(); } 訪問靜態成員函數的方式有兩種: 1. 與普通成員函數訪問的方式一樣,可以用對象、指針和引用來訪問 2. 靜態成員函數所屬類類名::靜態成員函數,即int class::volume(); 3. 靜態成員函數中沒有this指針,而是用“類名::”來代替this,因此靜態成員函數中只能夠訪問類中的靜態成員變量。 例如:class Stu { public: static int a;//靜態成員變量 int b; int c; static void static_m1(); static void static_m2(); void m3(); void m4();}; void Stu::static_m1() { cout<<"Stu::static_m1()\n"; Stu::a = 23; //可省Stu:: Stu::static_m2(); //可省Stu::} void Stu::static_m2() { cout<<"Stu::static_m2()\n";} void Stu::m3() { this->static_m1(); Stu::static_m1();} void Stu::m4() {} main() { Stu::static_m1(); Stu::static_m2(); Stu stu1; stu1.static_m1(); stu1.static_m2(); } static 的總結 1. static修飾的成員可以像普通的成員一樣被訪問(對象.成員,對象指針->成員,引用) 2. static修飾的成員函數中不能通過this訪問普通的成員,但是可以通過類名::訪問靜態成員 3. 普通的成員函數中可以通過this訪問靜態成員,也可以通過類名::訪問靜態成員 十四、訪問權限修飾符 public(公有的)修飾的成員沒有限制,都可以訪問 protected(受保護的)修飾的成員變量只能夠在當前類的(當前類的成員函數)或子類中訪問 private(私有的)修飾的成員變量只能夠在當前類(當前類中的成員函數)中使用 十五、友元 在一個類中可以有公用的(public)受保護的(protected)和私有的(private)成員,還有一個例外——友元(friend) 類中的私有成員可以在以下四個地方被訪問: 1.當前類中 2.類的友元函數; 3.類的友元成員函數; 4.類的友元類的成員函數; 類的友元函數就是在類中把普通函數用friend進行聲明 例如: class Stu{ friend void test2(); Private: int age; void show(); } void test1(){ Stu stu; stu.age;//錯誤,私有成員不可以調用 Stu.show(); } void test2(){ Stu stu; Stu.age=23; Stu.show();}//正確,友元函數中可以訪問私有成員 類的友元成員函數就是在類中把另一個類的成員函數用friend進行聲明,例如: class Stu{ friend void Teacher::test2(); private: int age; void show(); } class Teacher{ public: void test1(); Void test(); } Void Teacher::test1(){ Stu stu; stu.age=2;//錯 stu.show();//錯 } Void Teacher::test2(){ Stu stu; stu.age=2; stu.show();//對 } 類的友元類,類的友元類就是在類中把另一個類用friend進行聲明,例如: class Stu{ friend class Teacher private: int age; void show(); } class Teacher(){ public: void test1(); void test2(); } Void test1(){ Stu stu; stu.age=2; stu.show();//對 } void test2(){ Stu stu; stu.age=2; stu.show();//對 } 十六、繼承與派生 一個派生類只從一個基類派生,這稱為單繼承。一個派生類從多個基類派生叫做多重派生 派生類的聲明方式:class 派生類名:【繼承方式】 基類名……【基類n繼承方式】基類n類名{ 派生類新增加成員 }; 假設已經聲明了一個基類Student,在此基礎上通過繼承建立一個派生類Student1: class Student1:public Student{ public: int agt; string addr; public: void display(); } 繼承方式包括: public (公用的) private(私有的) protected(受保護的) 不寫繼承方式的情況下默認為private(私有的) 派生 派生類中的成員包括從基類繼承過來的成員派生類新增加的成員 派生類不會吸收基類的構造函數和析構函數,派生過程為: 基類成員權限修飾類與繼承方式對繼承的作用 Public繼承 Protected繼承 Private繼承 基類 private protected public private protected public private protected public 派生類 protected public protected protected private private 由上面的表格可以知道,基類中的private修飾成員不能夠繼承,public繼承將public繼承為public、protected繼承為protected,protected繼承將基類中protected和public全部繼承為protected,private繼承將基類中的public和protected全部繼承為private。 派生類的構造函數和析構函數 1.派生類的構造函數和析構函數不能夠被重寫; 2.派生類的構造函數被調用時,會先調用基類中的一個構造函數,因為在派生類的構造函數用了初始化表的方式調用了基類構造函數 3.在派生類對象釋放時,先執行派生類析構函數,再執行基類析構函數。 派生類構造函數的形式: 派生類構造函數名(總參數列表):基類構造函數名1(參數列表)……基類構造函數名n(參數列表) 十七、繼承的二義性 多基繼承的二義性 class A1{ public: int a int ; void m(); } class A2{ public: int a; voidm(); } class B:public A1,public A2{ } main(){ B b; b.2=2;//錯 b.m();//錯 b.A1::a=2//對 b.A!::m();//對 } 由於一個派生類有多個基類,並且基類中的函數形式一樣,這種想叫做由於多基繼承產生的二義性,這種情況下在派生類中訪問產生二義性的基類中的成員時候需要在成員變量前面指定所在的類名和雙冒號(::)。 共同基產生的二義性 如果派生類的基類是一模一樣的類,在派生類訪問基類的成員變量時候也會出現二義性,那麼這種情況叫做共同基產生的二義性。 例如: class B1:public A{}; class B2:public A{}; class C:public B1,public B2{}; 共同基產生的二義性的解決辦法——虛基派生 例如: class B1virtual :public A{}; class B2:virtual public A{}; class C:public B1,public B2{}; 這樣的話在派生類C中只會出現一個A類,可以直接訪問基類的變量不會出現二義性。 派生類對象為基類對象賦值 (1) 派生類對象可以向基類對象賦值。 假如A是B的基類: A a1; B b1; a1=b1; 派生類對象可以為基類對象賦值,這個時候只會把從基類繼承過來的成員復制過去,而把派生類自己新增的成員捨棄不用; (2) 基類聲明的指針變量和引用類型變量可以用來指向派生類的對象,但是此時只能通過指針或引用訪問從基類中繼承下來的成員函數和成員變量 假如A是B的基類: B b; A *pa=&b; A &ra=b; 這個時候指針或引用只能夠訪問從基類A中繼承下來的成員函數和成員變量 十八、組合 1.在A類中以B類的對象作為成員變量的,成為類的組合。 2.在組合時,一般A類的構造函數初始化列表中要初始化B類(會調用B類的一個構造函數),如果沒有初始化B類,則調用B類中可以不傳參數的構造函數。 例如: class A{ public: A(); } class B{ public: B(int); B(int a,int b); } class C{ public: A a; B b; C(int x,int y); } C::C(int x,inty):b(x){}; //C::C(int x,int y):b(x,y){}; //C::C(int x,int y):a(),b(x){}; //C::C(int x,int y):a(),b(x,y){}; 十九、多態性 多態性是面向對象程序設計的一個重要特征。利用多態性可以設計和實現一個易於擴展的系統。多態性是指具有不同功能的函數可以用同一個函數名,這樣就可以用一個函數名調用不同內容的函數。向不同的對象發送同一個消息,不同的對象在接到是會產生不同的行為。 多態性分為動態多態性和靜態多態性 函數重載和運算符重載實現的多態性屬於靜態多態性,在程序編譯時系統就能夠決定用的是哪個重載函數,因此靜態多態性又稱為編譯時的多態性。靜態多態性是通過函數的重載實現的。 動態多態性是在程序運行的過程中才動態地確定操作所針對的對象。它又稱為運行時的多態性。動態多態性是通過虛函數實現的。 下面來討論一下虛函數,在討論虛函數之前必須要弄清楚兩個概念 二十、隱藏和重寫 隱藏 1.派生類中的成員函數與基類中的成員函數名字相同時,派生類的成員函數會屏蔽基類中同名的成員函數,這種現象叫做隱藏。 2.派生類中的成員變量與基類中的成員變量同名時,派生類的成員變量屏蔽基類中同名的成員變量 3.通過派生類對象、指針、或引用訪問基類中被隱蔽的成員時,要在成員前面加上“基類名::” 例如: //Base.h class Base{ public: int x; void show(); void show(int x); } //Child.h class Child:public Base{ public; char x; int show(string name); } int main() { Child child; child.x=’c’;//child child.Base::x=1234;//Base child.x=1234;//錯,這樣訪問的是child中的x child.show(“tcc”);//Child child.Base::show(23);//Base child.show();//錯 參數不對,訪問的是child中的show() } 重寫 重寫是隱藏的一種特殊情況,當派生類中的成員函數與基類的的名字相同,參數相同,返回值類型相同或者類型兼容,則稱為派生類重寫了基類函數。 //Base.h class Base{ public: void show(); void show(int x); } //Child.h class Child:public Base{ public; void show(); } int main(){ Child child; child.Base::show(12);//Base child.show();//Child } 二十一、虛函數 1.在聲明函數時,在最前面加上virtual,則該函數就是虛函數,基類的虛函數被派生類繼承後仍是虛函數。 2.派生類中可以重寫基類的虛函數。 3.用指針訪問或引用虛函數時,被訪問的虛函數是指針指向的對象所屬類的函數(只看指向的對象所屬類)。而指針或引用訪問重寫的普通函數時,被訪問的函數是指針或引用類型所屬類的函數(只需要看指針或引用類型是什麼類) 4.虛函數可以先行動態關聯 例如: class Base { public: void show1(); virtual void show2(); }; class Child :public Base{ public: void show1(); virtual void show2(); int main() { Child child; Base *p=&child; p->show1();//Base p->show2();//Child Base &r=child; r.show1();//Base r.show2();//Child return 0; } 二十二、虛析構函數 (1)虛析構函數即:定義聲明析構函數時在前面加virtual修飾,如果將基類的析構函數聲明為虛析構函數時,由該基類所派生的所有派生類的析構析構函數也都是自動成為虛析構函數 (2)基類指針p指向用new動態創建的派生類對象child時,用“delete p”刪除對象時分兩種情況 第一、如果基類中的析構函數為虛析構函數,則會先刪除派生類對象,再刪除基類對象 第二、如果基類中的析構函數為非虛析構函數時,則會刪除基類對象,不會刪除派生類對象,這樣會出現內存洩露的問題,這個很重要,所以一般把析構函數聲明定義成虛析構函數。 二十三、純虛函數 純虛函數的定義: (1)虛函數被“初始化”為0的函數。聲明純虛函數的一般形式是 virtual 函數類型 函數名(參數列表)=0; (2)純虛函數沒有函數體,由函數體的話就沒有意義了 (3)最後面的“=0”並不是表示函數的返回值為0,它只是起了形式上的作用,告訴編譯系統“這是純虛數函數” (4)這是一個聲明語句,最後必須加分號 (5)不能夠在當前類中定義 二十四、抽象類 (1)包含有純虛數函數的類都是抽象類。 (2)不能夠用抽象類創建對象(這一點很重要),但是可以用抽象類派生出派生類。也可以用抽象類創建指針或引用來指向抽象類的派生類對象 (3)抽象類的派生類可以把抽象類中的成員變量和成員函數繼承下來,包括純虛函數也會被繼承 (4)抽象類的派生類可以實現抽象類的純虛函數。如果抽象類的派生類沒有吧抽象類的純虛函數實現,那麼這個派生類也是抽象類。 (5)純虛函數被派生類實現以後變成了虛函數 二十五、Protected或private修飾的構造函數 在類的外部創建對象時,不能夠調用protected或private修飾的構造函數。 當子類中的構造函數調用父類的private修飾的構造函數時會出錯,當子類中的構造函數調用父類中的public或protected修飾的構造函數時是對的 二十六、域名空間 域名空間就是在函數名前面加上自己的名字 例如: namespace tt{ class Stu { public: void study(); }; } namespace cc{ class Stu { public: void study(); }; namespace tt{ void Stu::study() { cout << "Stu::study() tt \n"; } } namespace cc{ void Stu::study() { cout<<"Stu::Study() cc \n"; } } cout和cin函數的域名就是std,所以在使用的時候需要先聲明 using namespace std;如果不聲明那麼在使用cout和cin時需要在函數前面加上”std::”,同理在使用其他自定義的域名空間的時候也需要在使用前聲明,或者在使用時加上域名雙冒號。使用域名空間可以避免出現同名的函數時會出現歧義的情況,在大型項目時都會用到域名空間