C++基礎知識篇

2020-08-13 00:45:04

一.知識點梳理

1. ubuntu下安裝g++編譯器

  • 在終端輸入指令
sudo apt-get install build-essential

可以通過g++ -v檢視是否安裝成功

2. 過載

2.1 過載的概念

  • 出現在相同作用域中的函數,有相同的函數名,而形參表不同,稱爲過載函數
int max(int a, int b);
double max(double a , double b);
char* max(char* a, char* b);

2.2 函數過載與重複宣告的區別

  • 兩個函數的返回型別和形參完全匹配,則第二個函數宣告稱爲第一個的重複宣告,會報錯

2.3 操作符過載

2.3.1 過載操作符的意義

  • 比如說一個類中有兩個物件,如何實現兩個物件的操作? ------ 對操作符進行過載,按照自己的意願來操作兩個物件或者物件

2.3.2 過載操作符的定義

  • 定義方法
    函數型別 operator 操作符名稱 ( 形參表列 )
    {
    對操作符的過載處理
    }
  • 舉個列子,要實現複數相加
Complex operator + ( Complex&  lc, Complex& rc)
{ 
	Complex c;
	c. real = lc.real + rc.real;
	c. img = lc.img + rc.img;
	return c; 
}
  • 可過載的操作符
    在这里插入图片描述
  • 不可以過載的操作符
. .* :: ? sizeof

2.3.3 運算子過載的規則

  1. 過載操作符的函數的參數必須至少有一個是該類型別的物件
  2. 操作符過載不能改變操作符的結合性和優先順序
  3. 過載操作符函數不能有預設的參數
  4. 有一些操作符在過載的時候需要謹慎對待

= & , && ||
如果不在類中定義賦值操作符過載,則編譯器會自動的合成一個,按照類中定義的順序,按每個成員的賦值操作符依次對每個成員進行賦值。
取地址操作符不需要過載就可以返回類物件的記憶體起始地址。
逗號操作符不需要過載就可以從左至右計算每個表達式的值,並返回最右邊運算元的值。
邏輯與和邏輯或操作符有短路求值的特徵,如果過載則失去短路求值的特徵。

  1. 過載操作符的兩種形式
    (1)類的成員函數

賦值(=)、下標([ ])、呼叫()、成員存取(->)必須定義爲類的成員函數
複合的賦值操作符(+= -+ = /=)優先定義爲成員函數,因爲返回的是左運算元本身
改變物件的狀態或與給定型別緊密聯繫的操作符,如自增、自減,解除參照
等單目運算子,優先定義爲成員函數

(2)類的友元函數

輸入操作符>>, 輸出操作符<<必須定義爲類的友元函數
對稱的操作符,如算術操作符,關係操作符,位元運算符等雙目運算子,優先定義成類的友元函數。
關係操作符過載函數必須返回true(非0)或false(0),即bool型別的值

2.4 算數運算子過載

  • 以加法爲例子,優先定義算術操作符爲友元函數。
Complex operator + (const Complex& c1,const Complex& c2)
{
	Complex sum;
	sum.real = c1.real + c2.real;
	sum.img = c1.img + c2.img;
	return sum;
}

對於既定義了算術操作符又定義了複合賦值操作符的類,優先使用複合賦值實現算術操作,可以不必建立和釋放一個臨時變數儲存算術操作的結果。

2.5 關係運算符過載

  • 優先定義關係操作符爲友元函數
  • 關係操作符過載函數必須返回true(非0)或false(0),即bool型別的值。
bool operator == (const Complex& c1,const Complex& c2)
{
	if(c1.real == c2.real && c1.img == c2.img)
		return true;
	else 
		return false;	
}

2.6 關係運算符過載

  1. 必須定義賦值操作符爲成員函數,以便編譯器知道是否需要合成一個預設的賦值操作符過載函數。
  2. 當類中有指針型別的數據成員或者有動態記憶體分配時,必須顯式的定義賦值操作符。
  3. 爲了滿足連續賦值,賦值操作符返回當前類物件的參照。
Complex& Complex::operator = (const Complex& c1)
{
	if(this != &c1)
	{
		real = c1.real;
		img =  c1.img;
                  }
	return *this;		
}

2.7 輸入輸出操作符過載

  • 以輸出操作符爲例,輸入與輸出類似
  1. ostream是C++標準庫的輸出流類,cout是這個類的物件。表示輸出到標準輸出裝置。
  2. 成員函數要求第一個運算元必須爲該類物件,因此輸出操作符必須定義爲友元函數。
  3. 返回值型別爲ostream類物件的參照,表示將輸出流的現狀返回,這樣就可以進行連續的輸出。
  4. 輸出操作符裏面所做的格式化應該儘量少,最好不要輸出換行符。
ostream& operator  << ( ostream& out, const Complex & c)
{
	out<<(<<c.real<<,<<c.img<<);
	return out;
}

2.8 自增自減操作符過載

  1. 優先定義爲成員函數。
  2. 注意前置運算和後置運算的區別。
  3. 前置運算應該返回被增量物件的參照。
  4. 後置運算應該返回未增之前的物件。
Complex& Complex::operator++()   //前置運算
Complex Complex::operator++(int)   //後置運算

2.8 呼叫操作符過載

  1. 爲表示操作的類過載呼叫操作符。
  2. 呼叫操作符必須定義爲類的成員函數。
  3. 呼叫操作符可以過載,由形參的數目或型別加以區別
  4. 定義了呼叫操作符的類,其類物件稱爲函數物件。
  5. 函數物件常用於標準庫的演算法。
    一般形式:
    <函數型別> 類名:: operator()(<形參表列>);
  6. 舉個例題
//定義一個類Abs,這個類封裝將int型別,double型別的物件轉換爲絕對值的操作。用呼叫操作符過載實現。 
#include <iostream>
using namespace std;
class Fbs
{
public:
	int operator()(int num){
		if (num >= 0){
			return num;
		}else{
			return -num;
		}
	}
	double operator()(double num){
		if (num >= 0){
			return num;
		}else{
			return -num;
		}
	}
};
int main(void){
	Fbs f;
	cout << f(-666) << endl;
	cout << f(666) << endl;
	cout << f(-666.666) << endl;
	return 0;
}

3. 參照

3.1 參照的概念

int a = 10;
int &b = a;

b稱爲a的參照,b是a的別名;參照變數b與變數a系結在一起,也叫相關聯。
&表示參照宣告符,不是取地址符;b和a佔同一記憶體儲存單元。

  • 參照時的注意事項

1.定義一個參照時必須同時進行初始化
2.當參照初始化後,只要該參照存在,它就與初始化時指向的物件相關聯,不能將參照關聯到其它物件。
3.參照不是獨立的數據型別,是複合型別,必須使其代表某一型別的物件。
4.可以定義陣列參照

3.2 參照作爲函數參數

  • 參照與非參照型別作爲函數參數的區別?

形參具有非參照型別(不論是內建型別還是指針型別),形參複製實參的值。
形參爲指針型別時,形參複製的是指針所指向物件的地址,所以函數內部可以通過對指針的使用,來修改指針所指向物件的值。
形參爲參照型別時,它只是實參的別名,並不是實參的副本。
呼叫函數時,參照形參被建立,並且與相應的實參相關聯,對形參的任何修改其實就是對實參的修改。
在C++中,建議多使用參照作爲函數參數,比指針更方便

  • 使用參照形參的情況

需要在函數中改變實參的值
大型的物件(如類物件)作爲實參傳遞時,此時複製物件所需要的時間和空間的代價都較大。

  • 以下一種情況必須使用參照形參:

物件無法進行復制時。(比如IO流物件)

3.3 參照作爲函數返回值

  • 函數的返回值是用於初始化在函數呼叫處建立的臨時物件
    在这里插入图片描述
  • 參照作爲函數返回值需要注意的問題

函數返回參照型別時,不會複製返回值,返回的是物件本身。
不要返回系結區域性物件的參照,函數呼叫結束,區域性物件被釋放,返回的參照就會指向不確定的記憶體。
不要返回指向棧記憶體的指針,因爲函數呼叫結束,區域性物件被釋放,返回的指針變成了指向不再存在物件的懸垂指針。

二.重難點

1.參照和指針的區別?

  • 從記憶體分配上來看:指針是一個實體,而參照僅是個別名。程式爲指針變數分配記憶體區域,而參照不需要分配記憶體區域,它與它所系結的物件佔同一記憶體儲存單元。
  • .「sizeof 參照」得到的是參照所系結的物件的大小,而「sizeof 指針」得到的是指針本身的大小;
  • 參照使用時無需解除參照(*),指針需要解除參照;
  • 參照只能在定義時被初始化,之後就不能改變它和它所系結的物件之間的聯繫,指針可改變指向。

2. 賦值操作符爲什麼必須被定義爲成員函數?

定義爲成員函數可以便於編譯器知道是否需要合成一個預設的賦值操作符過載函數

3.輸出操作符的返回值是什麼型別?這樣的目的是什麼?

輸出操作符的返回值型別是ostream的參照,是c++標準庫的輸出流類,這樣的目的是表示將輸出流的現狀返回,這樣就可以進行連續的輸出。

4.輸出操作符爲什麼必須被定義爲友元函數?

因爲成員函數要求第一個運算元必須爲該類的物件