天天看點

C++篇彙總C++篇彙總

C++篇彙總

(一)C++之起航篇

初識C++

第一章. 初識C++

(一)

C++的起源:

1.誕生地:貝爾實驗室(Bell Lab)

2.C++之父:比雅尼 • 斯特勞斯特魯普博士(為人低調,有問必答)

C++的應用領域:1. 嵌入式,如手機,電視機等、2. 遊戲程式設計 、3. 網絡程式設計、4. 系統程式設計
C++與C的關系:

1.從文法角度上來講:C是C++的子集

2.從曆史角度上來講:C++ 是從C的基礎上發展而來的, C語言面向過程, 而C++支援面向過程+支援面向對象

PS:c語言更接近低端語言,容易識别,故比C++運作更高效

3.學習C++需要C語言的基礎,相關網址為:http://imooc.com/learn/249

C++ IDE環境搭建:

1.IDE環境:Integrated Development Environment(內建開發環境)一般包括代碼編輯器、編譯器、調試器和圖形使用者界面等工具、一般常使用的工具:Visual Studio 2010旗艦版、Visual Assist X for Visual Studio、Dev C++、CLion等

2.IDE初體驗——Visual Studio 2019的使用中存在的問題:

安裝問題:

為了圖節省的硬碟空間,少下了一些東西,例如NET的移動開發,和個别C++ 程式應用過程中的必要負載,如C++桌面開發 ,針對已安裝好,才後悔的人(例如我(~ ̄▽ ̄)~)我提供了以下解決方案:
           

首先,在該路徑(C:\Program Files (x86)\Microsoft Visual Studio\Installer)中找一個叫setup的應用程式,也就是Visual Studio 2019的安裝程式(一般這個路徑是預設的,你就算在安裝時換了安裝路徑,安裝完以後,它依然在哪裡);其次,打開後你可以看到在Visual Studio哪裡有一個修改按鈕,之後便可以進行修改,重新再來了。

閃跳問題:

在你的項目(project)上右擊滑鼠,在彈出菜單上選擇最後一項“property/屬性”,在左邊的一欄裡找到“配置屬性->連結器->系統”,點選“系統”項;在右邊的欄的“子系統(subSystem)”将刻項的值配置為"Console(控制台/SUBSYSTEM:CONSOLE)"。

C++的基本了解

  • 新資料類型
    • 新邏輯類型bool(true 和false):消除了C中隻能用int類型(0或1)來判斷真假的情況
  • 新的初始化方法
    • 複制初始化(同C) int x=1024
    • 直接初始化 int x(1024)
  • 随用随定義
  • 輸入和輸出方式
    • 輸入:cin
    • 輸出:cout
    • Ps: oct、dec、hex分别是8進制、10進制、16進制的英文縮寫,如輸出一個八進制數(C++): cout<<oct<<x<<endl;輸出bool值的符号:boolalpha)
  • 命名空間(namespace)

    即劃片取名字

    樣例:

    定義: 輸出調用:

namespace A   
cout<<A::x<<endl;
{  
A::f1();               		 
  int x=0;
  void f1();
  void f2();
}
           

注意:命名空間的名字是不可以重複的,如使用cout和cin函數時,要包含iostream頭檔案和using namespace std;命名空間

(二) C++之離港篇

引用

即變量的一個别名,一個變量不可能隻有别名,否則,别名即是該真名

變量類型的相關引用:

  • int a=3; int &b=a;//引用必須初始化

結構體類型的相關引用: typedef struct{int x;int y;}Coor;

引用方式: Coor c1;(對象執行個體化)

Coor&c=c1

指針類型的引用

*&指針引用名=指針;

int a=10; int *p=&a;(這裡p取a的值,為10) int *&q=p;

引用作函數參數

void fun(int *a, int *b)           在使用引用後:
{                                                   void fun(int &a, int &b) 
int c=0;                                    {
   c=*a;                                        		 
int c=0;
   *a=*b;                                          c=a;
   *b=c;                                           a=b;
   }                                                       b=c;}
   在調用上:                                  在調用上:
   int x=10,y=20;                              int x=10,y=20;
   fun(&x,&y);                                   fun(x,y);
           

注意:引用不能單獨存在,如int &a; int b;就是錯的

const

起控制變量的作用,使目标變量成為一個常量,使其不可被改變

  • const與指針類型
    • const int*(const)p=NULL;
    • int const* (const) p=NULL
    • int*const p=NULL; 限定了p
    補充:const int x=3;const int *const p=&x;//p=&y;*p=4;都是錯誤的

原因是,const使x和p都變成了常量,均不可被改變,也不可被變量引用

  • const與引用
    • int x=3;const int &y=x;//x=10;正确 //y=20;錯誤

一些函數新特性

  • 函數參數預設值
    • 有預設參數值的參數必須在參數表的最右端

      如:void fun(int i,int j=5,int k=10);

    • 無實參則用預設值,否則實參覆寫預設值
  • 函數重載:在相同作用域内,用同一函數名定義的多個函數,且在多個函數中參數個數和參數類型是不同的

    例如:int getMax(int x, int y, int z){ //to do} /double getMax(double x,double y){//to do}

    計算機編譯好後形成的新的參數:

    getMax_int_int_int

    getMax_double_double

    • 優勢:1) 提高了代碼命名時的準确性和高效性;2) C++的重載的兩個函數參數數量可以相同也可以不同,當參數數量相同時,隻需要對應參數類型不同即稱為重載

内聯函數 (編譯時将函數體代碼和實參代替函數調用語句)

内聯函數關鍵字:inline

例子:

inline int max(int a,int b,int c);
  int main()
  {
  	int i=10,j=20,k=30,m;
  	m=max(i,j,k);
  	代碼展開:
  	int a,b,c;
  	a=i;b=j,c=k;
  	if(b>a) a=b;
  	if(c>a) a=c;
  	m=a;
  	cout<<"max="<<m<<endl;
  	return 0;
  }

  減少了2和4的過程
           

注意點:

->内聯編譯是建議性的,由編譯器決定

->邏輯簡單,調用頻繁的函數建議使用内聯

->遞歸函數無法使用内聯方式

記憶體管理

申請/歸還記憶體資源就是記憶體管理

記憶體的申請和釋放

内容總結

使用new申請記憶體,使用delete釋放記憶體

申請記憶體需要判斷是否成功,釋放記憶體需要設空指針

new與delete配套使用

  • 申請記憶體

    運算符new :

int *p=new int(20);/  int *arr=new int[10];
    注意:int *p = new int [20]; delete []p;申請的是空間數組,而
    int *p = new int (20); delete p;申請的是記憶體空間
           

釋放記憶體

  • 運算符delete :

    delete p;/ delete[]arr;

  • 申請記憶體注意事項:

    int *p=new int [1000];

    if(NULL==p){//記憶體配置設定失敗}

C++之封裝篇

  • 封裝篇(上)

    • 類和對象
      • 類的定義
關鍵字->class Dog<——類名
      {
      	char name[20];
      	int age;			<——資料成員(屬性)
      	int type;

       	void speak();
       	void run(); 		<————————成員函數(方法)
       	};
       	類是抽象的概念,對象是具體的事物
       	定義類應當使用關鍵字class
           

目的不同抽象出的資訊不同(選擇性暴露,即封裝)

通路限定符
public: 公共的
  protected: 受保護的
  private: 私有的
           
對象執行個體化
從棧執行個體化
  從堆執行個體化
           
初識string
字元串類型:string
           
栗子:
    #include<iostream>
    #include<string>
    using namespace std;
    int main()
    {
    	string name="ZhangSan";
    	string hobby("football");
    	cout<<name<<hobby<<endl;
    	return 0;
    }
           

拓展:1)檢驗使用者輸入是否為空時,要使用getline函數;2)name.empty()函數,判斷名字是否為空;3)name.size()函數,判斷名字的長度

資料的封裝
>面向對象的基本思想: 對象的所有行為都通過調用函數完成

>定義在類裡面的變量規範,如string類型的,寫為m_strName;   int類型的,寫為:m_iName
           
  • 類外定義

    類内定義與内聯函數:即成員函數和函數體寫在類的裡面,類内定義的函數優先選擇編譯為内聯函數

    類外定義:即成員函數和函數體寫在類的外面,它分為:同檔案類外定義和分檔案類外定義

構造函數
記憶體分區

  棧區: int x=0; int *p=NULL;
  堆區: int *p = new int[20];
  全局區: 存儲全局變量及靜态變量
  常量區: string str = "hello";
  代碼區:存儲邏輯代碼的二進制
           

對象初始化

有且僅有一次
  根據條件初始化
           

構造函數的規則和特點

- 構造函數在對象執行個體化時被自動調用
  - 構造函數與類同名
  - 構造函數沒有傳回值
  - 構造函數可以有多個重載形式
  - 執行個體化對象時僅用到一個構造函數
  - 當使用者沒有定義構造函數時,編譯器自動生成一個構造函數
           

無參構造函數

class Student
{
  public:
  Student(){m_strName = "jim";}
  private:
  string m_strName;
};
           

預設構造函數

即在執行個體化對象時不需要傳遞參數的函數, 一個類可以沒有預設構造函數,有别的構造函數也可以執行個體化對象

構造函數初始化清單

初始化清單

class Student
{
public:          	Student():m_strName("Jim"),m_iAge(10){}
private:
  string m_strName;
  int m_iAge;
 };
           

初始化清單特性

初始化清單先于構造函數執行
 初始化清單隻能用于構造函數
 初始化清單可以同時初始化多個資料成員

 初始化清單存在的必要性
           

有參構造函數

class Student
      {
      public:
      	Student(string name)
      	{m_strName = name;}
      private:
      	string m_strName;
      };
           

參數帶預設值

參數無預設值

重載構造函數

class Student
      {
      public:
      	Student(){m_strName = "jim";}
      	Student(string name)
      	{m_strName = name;}
      private:
      	string m_strName;
      };
           
系統自動生成的函數

拷貝構造函數

定義格式:類名(const 類名&變量名)
 class Student
 {
 public:
    Student(){m_strName = "jim";}
    Student(const Student& stu){}
 private:
    string m_strName;
 };
           

注意點:

1)如果沒有自定義的拷貝構造函數則系統自動生成一個預設的拷貝構造函數

2)當采用直接初始化或複制初始化執行個體化對象時,系統自動調用拷貝構造函數

3)拷貝構造函數是确定的,不能重載

普通構造函數

析構函數

定義格式: ~類名()
      class Student
      {
      public:
      	Student() {m_pName = new char[20];}
      	~Student() {delete [] m_pName;}
      private:
      	char *m_pName;
      };
           

如果沒有自定義的析構函數則系統自動生成

析構函數在對象銷毀時自動調用

析構函數沒有傳回值、沒有參數也不能重載

封裝篇(下)

對象成員與對象數組

對象數組

定義:
class Coordinate
{
  public:
  int m_iX;
  int m_iY;
 };
使用時:
  int main(void)
{
   Coordinate coord[3]; //棧上
   coord[1].m_iX=10;
   Coordinate *p = new Coordinate[3]; //堆中
   p[0].m_iY = 20; p->m_iY = 20;
   delete []p;
   p = NULL;
   return 0;
 }
           

注意點:

1. 堆中執行個體化的數組需要手動銷毀釋放記憶體,在棧中執行個體化的數組,系統自動回收記憶體
    2. 執行個體化對象數組時,每一個對象的構造函數都會被執行。
    3. 執行個體化對象數組時,記憶體既可以從堆上配置設定,也可以從棧上配置設定。
    4. 銷毀對象數組時,每一個對象的析構函數都會被執行。
           

對象成員

注意點

1. 坐标類的構造函數是需要有參數的
    2. 對象成員在調用構造函數的順序是從裡往外,在調用析構函數的時候是從外往裡
           

點的定義

class Coordinate
{
   public:
   Coordinate(int x, int y);
   private:
   int m_iX;
   int m_iY;
 };
           
線段的定義
           
class Line
 {
   public:
   Line();
   private:
   Coordinate m_coorA;
   Coordinate m_coorB;
  };
           

深拷貝與淺拷貝

淺拷貝
例子:
      class Array
      {
      public:
      	Array(){m_iCount = 5;}
      	Array(const Array& arr)
      	{m_iCount=arr,m_iCount;}
      private:
      	int m_iCount;
      };
      int main(void)
      {
      	Array arr1;
      	Array arr2=arr1;(在這裡,arr1=m_iCount,而arr2=arr,m_iCount;)
      	return 0;
      }
           
淺拷貝所帶來的劣勢:

    當拷貝指針類型的相關函數時,會使得拷貝的函數與原先的函數指向同一個位址,進而可能造成系統的崩潰
           
深拷貝
小知識:快速注釋掉代碼的快捷鍵:Ctrl+k+c(适用于Visual Studio)
           

對象指針

即用指針來指向一個對象
           
int main(void)
    {
    	Coordinate *p=new Coordinate;
    	p->m_iX=10; //(*p).m_iX=10;
    	p->m_iY=20; //(*p).m_iY=20;
    	delete p;
    	p=NULL;
    	return 0;
    }
           

對象成員指針

定義:
      class Line
      {
      public:
      	Line();
      	~Line();
      private:
      	Coordinate *m_pCoorA;
      	Coordinate *m_pCoorB;
      };
           
記憶體中的對象成員指針

  一個指針占4個記憶體單元,一共占了八個
           

this指針

用于解決參數與資料成員同名所引起的系統報錯問題

class Array
{
  public:
  Array(int len){this->len=len;}
  int getLen(){return len;}
 void setLen(int Len){this->len=len;}
  private:
     int len;
};
           

this指針在參數清單中的位置

this指針的相關注意點

1. this指針無需使用者定義,是編譯器自動産生的。

2. 對象各自的this指針指向各自對象的首位址,是以不同對象的this指針一定指向不同的記憶體位址

3. 當成員函數的參數或臨時變量與資料成員同名時,可以使用this指針區分同名的資料成員。

4. this指針也是指針類型,是以在32位編譯器下也占用4個基本的記憶體單元,即sizeof(this)的結果為4。

const之再現江湖

常對象成員和常成員函數

常對象成員

class Line
{
  public:
  Line(int x1,int y1,int x2,int y2);
  private:
  const Coordinate m_coorA;
  const Coordinate m_coorB;
 };
           

常成員函數

class Coordinate
{
public:
  Coordinate(int x,int y);
  void changeX() const; <——常成員函數
  void changeX();
  private:
  int m_iX;
  int m_iY;
}
           

常指針與常引用

常指針
  const Coordinate *pCoor=&coor1;
  常引用
  const Coordinate &coor2=coor1;
           

注意點

  1. 一個對象可以有多個對象常引用
  2. 常指針和常引用都隻能調用對象的常成員函數
  3. 普通對象能夠調用常成員函數,也能夠調用普通成員函數
  4. 常對象隻能調用常成員函數,不能調用普通成員函數

C++遠征之繼承篇

什麼是繼承

class Worker: public Person
{
public:
	void work();
	int m_iSalary;
};
其中:
派生類(子類)——>class Worker
基類(父類)——>public Person
           

繼承的相關注意點

  1. 被繼承的類叫做基類也叫做父類,從其他類繼承而來的類叫做派生類也叫做子類。
  2. 子類中不僅繼承了父類的中的資料成員,也繼承了父類的成員函數。
  3. C++中的繼承關系是概念上的父子關系,不是個體的父子關系。
  4. 類與類之間必須遵循概念上的父子關系,否則将造成定義和使用的混亂

繼承方式

公有繼承

class A: public B

保護繼承

class A: protected B

私有繼承

class A: private B

相關注意點

  1. B類從A類派生,那麼B類是A類的子類,A類是B類的超類。
  2. B類從A類派生,那麼B類中含有A類的所有資料成員。
  3. B類從A類公共派生,那麼通過B類的對象隻能調用到A的公共及保護限定符下的成員函數
  4. B類從A類公共派生,那麼可以在B類中直接使用A的公共及保護限定符的資料成員。

繼承中的特殊關系

隐藏

關鍵字:父子關系、成員同名、隐藏

Is-a

存儲結構

虛析構函數

應用條件:當存在繼承關系時,我們需要使用父類的指針去隻指向堆中的子類的對象,并且還想使用父類的指針去釋放堆中的記憶體 virtual ~Person();

多繼承與多重繼承

多重繼承

class Person
  {
  };
  class Soldier: public Person
  {
  };
  class Infantryman: public Soldier
  {
  };
           

多繼承

class Worker
  {
  };
  class Farmer
  {
  };
  class MigrantWorker: public Worker, public Farmer
  {
  };
           

注意點

  1. 多繼承是指一個子類繼承多個父類
  2. 多繼承對父類的個數沒有限制,繼承方式可以是公共繼承、保護繼承和私有繼承。
  3. 多重繼承與多繼承不同,當B類從A類派生,C類從B類派生,此時成為多重繼承。

虛繼承

關鍵字:virtual
使用方法:
class Worker: virtual public Person
{
};
class Farmer: virtual public Person
{
};
           

小知識:用以解決重定義的相關bug

#ifndef 檔案名稱,如:#ifndef PERSON_H

#define 檔案名稱,如:#define PERSON_H

..........

#endif

C++之多态篇

虛函數及實作原理

虛函數

多态

什麼是多态

  指相同對象收到不同消息或不同對象收到相同消息時産生不同的動作

分類

  靜态多态(早綁定)
  動态多态(晚綁定)
           

virtual->虛函數

執行個體
           
class Shape
      {
      public:
      	virtual double calcArea() //虛函數
      	{
      		cout<<"calcArea"<<endl;
      		return 0;
      	}
      };
           

相關注意點

1. 多态具體到文法中是指,使用父類指針指向子類對象,并可以通過該指針調用子類的方法。
2. 産生多态的基礎是繼承關系,沒有繼承就沒有多态
3. 多态的文法核心是virtual關鍵字,必須使用virtual才能使多個類間建立多态關系。
4. 封裝、繼承、多态是面向對象的三大特性。
           

虛析構函數

多态中存在的問題

記憶體洩露
           
int main(void)
      {
      	Shape *shape1=new Circle(3,5,4.0);
      	shape1->calcArea();
      	delete shape1;
      	shape1=NULL;
      	return 0;
      }
           

virtual在函數中的使用限制

1)普通函數不能是虛函數
    2)靜态成員不能是虛函數
    3)内聯函數不能是虛函數
    4)構造函數不能是虛函數
           

注意點

1. 虛函數使用virtual關鍵字定義,但使用virtual關鍵字時,并非全部是虛函數
   2. 虛函數特性可以被繼承,當子類中定義的函數與父類中虛函數的聲明相同時,該函數也是虛函數。
   3. 虛析構函數是為了避免使用父類指針釋放子類對象時造成記憶體洩露。
           

virtual->析構函數

實作原理

函數指針

理論前提:執行完子類的析構函數就會執行父類的析構函數

注意點

  1. 在C++中多态的實作是通過虛函數表實作的
  2. 當類中僅含有虛析構函數,不含其它虛函數時,也會産生虛函數表
  3. 每個類隻有一份虛函數表,所有該類的對象共用同一張虛函數表
  4. 兩張虛函數表中的函數指針可能指向同一個函數。

純虛函數和抽象類

運作時類型識别

異常處理

C++遠征之模闆篇

友元函數和友元類

友元函數

關鍵字:friend

友元全局函數

class Coordinate
    {
    	friend void printfXY(Coordinate &c);
    public:
    	Coordinate(int x,int y);
    private:
    	int m_iX;
    	int m_iY;
    };
           

友元成員函數

class Coordinate
    {
    	friend void Circle::printXY(Coordinate &c);
    public:
    	Coordinate(int x,int y);
    private:
    	int m_iX;
    	int m_iY;
    };
           

友元類

class  Circle;
  class Coordinate
  {
  	friend Circle;
  public:
  	Coordinate(int x,int y);
  private:
  	int m_iX;
  	int m_iY;
  };
           

劣勢:易破壞函數的封裝性,且不易被察覺

關于友元的注意事項

  1. 友元關系不可傳遞
  2. 友元關系的單向性
  3. 友元聲明的形式及數量不受限制
  4. 友元隻是封裝的補充

靜态

關鍵字:static

普通資料成員與靜态資料成員的差別

靜态資料成員是依賴于類的,而普通資料成員則是依賴于對象的

注意事項

  1. 靜态資料成員必須單獨初始化
  2. 靜态成員函數不能調用非靜态成員函數和非靜态資料成員
  3. 靜态資料成員隻有一份,且不依賴對象而存在

運算符重載

給原有運算符賦予新功能

關鍵字:operator

一進制運算符重載

實際上就是隻與一個操作數進行運算
           

二進制運算符重載

模闆函數與模闆類

函數模闆

關鍵字: template 、 typename 、 class

例子

template<class T>
    T max(T a, T b)    <——函數模闆
    {
    	return( a>b )? a:b;
    }
    模闆函數:
    int ival=max(100, 99);
    char cval =max<char>('A', 'B');
           

類模闆

定義

template<class T>
    class MyArray
    {
    public:
    	void display()
    	{......}
    private:
    	T*m_pArr;
    };
           

類外定義

template<class T>
      void MyArray<T>::display()
      {
      	.......
      }
           
int main(void)
        {
        	MyArray<int> arr;
        	arr.display();
        	return 0;
        }
           

注意事項

  1. 定義一個類模闆就相當于定義了一系列功能相同類型不同的類
  2. 定義類模闆需要使用關鍵字template
  3. 定義類模闆的參數可以使用typename和class,可以混用
  4. 模闆參數既可以是類型,也可以是變量

标準模闆類

STL

注意點

1. vector是對數組的封裝,大小可以根據元素數量改變
2. map需要與pair一起使用,用來存儲多個key-value對。
3. 不同廠商的标準模闆庫的實作細節可以不同,基本用法及原理相同。
           

暫停函數:system("pause");