天天看點

C++學習筆記(十三)檔案操作

檔案操作

    • 1.文本檔案
      • 1.1寫檔案
      • 1.2讀檔案
    • 2.二進制檔案
      • 2.1寫檔案
      • 2.2讀檔案
    • 代碼:

//程式運作時産生的資料都屬于臨時資料,程式一旦運作結束都會被釋放

//通過檔案可以将資料持久化

//C++中對檔案操作需要包含頭檔案//檔案流

//檔案類型分為兩種:

//1.文本檔案:文本以文本的ASCII碼形式存儲在計算機中

//2.二進制檔案:檔案以文本的二進制形式存儲在計算機中,使用者一般不能直接讀懂它們

//操作檔案的三大類:

//1.ofstream:寫操作 o-output f-file

//2.ifstream:讀操作 i-input

//3.fstream:讀寫操作

1.文本檔案

1.1寫檔案

//1.包含頭檔案#include

//2.建立流對象 ofstream ofs;

//3.打開檔案 ofs.open(“檔案路徑”,打開方式);

//4.寫資料 ofs<<“寫入的資料”;

//5.關閉檔案 ofs.close();

//檔案打開方式:

//ios::in 為讀檔案而打開檔案

//ios::out 為寫檔案而打開檔案

//ios::ate 初始位置:檔案尾

//ios::app 追加方式寫檔案

//ios::trunc 如果檔案存在先删除,再建立

//ios::binary 二進制方式

//注意:檔案打開方式可以配合使用,利用 | 操作符

//例如:用二進制方式寫檔案 ios::binary | ios::out

//總結:

//檔案操作必須包含頭檔案 fstream

//讀檔案可以利用 ofstream ,或者fstream類

//打開檔案時候需要指定操作檔案的路徑,以及打開方式

//利用 << 可以向檔案中寫資料

//操作完畢,要關閉檔案

代碼:

/*文本檔案*/
//寫檔案

//1.包含頭檔案#include<fstream>
//2.建立流對象 ofstream ofs;
//3.打開檔案  ofs.open("檔案路徑",打開方式);
//4.寫資料 ofs<<"寫入的資料";
//5.關閉檔案  ofs.close();

//檔案打開方式:
//ios::in 為讀檔案而打開檔案
//ios::out 為寫檔案而打開檔案
//ios::ate 初始位置:檔案尾
//ios::app 追加方式寫檔案
//ios::trunc 如果檔案存在先删除,再建立
//ios::binary 二進制方式
//注意:檔案打開方式可以配合使用,利用 | 操作符
//例如:用二進制方式寫檔案 ios::binary | ios::out

void test_xwj01()
{
	ofstream ofs;
	ofs.open("test.txt", ios::out);
	ofs << "姓名:張三" << endl;
	ofs << "性别:男" << endl;
	ofs << "年齡:18" << endl;
	ofs.close();
}
//總結:
//檔案操作必須包含頭檔案 fstream 
//讀檔案可以利用 ofstream ,或者fstream類 
//打開檔案時候需要指定操作檔案的路徑,以及打開方式 
//利用 << 可以向檔案中寫資料 
//操作完畢,要關閉檔案
           

main函數:

/*文本檔案*/
	//寫檔案
	test_xwj01();
           

1.2讀檔案

//1.包含頭檔案 #include

//2.建立流對象 ifstream ifs;

//3.打開檔案并判斷檔案是否打開成功 ifs.open(“檔案路徑”,打開方式); 通過ifs.is_open()來判斷

//4.讀資料 四種方式讀取

//5.關閉檔案 ifs.close();

//總結:

//讀檔案可以利用 ifstream ,或者fstream類

//利用is_open函數可以判斷檔案是否打開成功

//close 關閉檔案

代碼:

//讀檔案
//1.包含頭檔案 #include<fstream>
//2.建立流對象 ifstream ifs;
//3.打開檔案并判斷檔案是否打開成功  ifs.open(“檔案路徑”,打開方式); 通過ifs.is_open()來判斷
//4.讀資料   四種方式讀取
//5.關閉檔案  ifs.close();

void test_dwj01()
{
	ifstream ifs;
	ifs.open("test.txt",ios::in);
	if (!ifs.is_open())
	{
		cout << "檔案打開失敗" << endl;
		return;
	}

	//讀資料
	第一種
	//char buf[1024] = { 0 };
	//while (ifs >> buf)//一行一行的讀,讀到頭以後傳回一個false
	//{
	//	cout << buf << endl;
	//}

	第二種
	//char buf[1024] = { 0 };
	//while (ifs.getline(buf, sizeof(buf)))
	//{
	//	cout << buf << endl;
	//}

	第三種
	//string buf;
	//while (getline(ifs, buf))
	//{
	//	cout << buf << endl;
	//}

	//第四種,不推薦用
	char c;
	while ((c = ifs.get()) != EOF)//EOF是end of file檔案尾,看是否讀到檔案尾
	{
		cout << c;
	}

	ifs.close();
}
//總結:
//讀檔案可以利用 ifstream ,或者fstream類 
//利用is_open函數可以判斷檔案是否打開成功 
//close 關閉檔案
           

main函數:

/*文本檔案*/
	//寫檔案
	test_dwj01();

           

2.二進制檔案

//以二進制的方式對檔案進行讀寫操作

//打開方式要指定為:ios::binary

2.1寫檔案

//二進制方式寫檔案主要利用流對象調用成員函數write

//函數原型ostream& write(const char * buffer,int len);

//參數解釋:字元指針buffer指向記憶體中一段存儲空間。len是讀寫的位元組數

//總結:

//檔案輸出流對象 可以通過write函數,以二進制方式寫資料

代碼:

//寫檔案
//二進制方式寫檔案主要利用流對象調用成員函數write
//函數原型ostream& write(const char * buffer,int len);
//參數解釋:字元指針buffer指向記憶體中一段存儲空間。len是讀寫的位元組數
 
class Person_xwj
{
public:

public:
	char m_Name[64];//姓名    不要用string,可能會出現問題,要用char
	int m_Age;//年齡
};

void test_xwj02()
{
	ofstream ofs;
	ofs.open("person.txt", ios::out | ios::binary);
	Person_xwj p = { "張三",18 };
	ofs.write((const char *)&p, sizeof(Person_xwj));
	ofs.close();
}
//總結:
//檔案輸出流對象 可以通過write函數,以二進制方式寫資料
           

main函數:

/*二進制檔案*/
	//寫檔案
	test_xwj02();
           

2.2讀檔案

//二進制方式讀檔案主要利用流對象調用成員函數read

//函數原型:istream& read(char *buffer, int len);

//參數解釋:字元指針buffer指向記憶體中一段存儲空間。len是讀寫的位元組數

代碼:

//讀檔案
//二進制方式讀檔案主要利用流對象調用成員函數read
//函數原型:istream& read(char *buffer, int len);
//參數解釋:字元指針buffer指向記憶體中一段存儲空間。len是讀寫的位元組數

class Person_dwj
{
public:
	char m_Name[64];
	int m_Age;
};

void test_dwj02()
{
	ifstream ifs;
	ifs.open("person.txt", ios::in | ios::binary);
	if (!ifs.is_open())
	{
		cout << "檔案打開失敗" << endl;
		return;
	}

	Person_dwj p;
	ifs.read((char*)&p, sizeof(Person_dwj));
	cout << "姓名:" << p.m_Name << "\t年齡:" << p.m_Age << endl;
	ifs.close();


}
           

main函數:

/*二進制檔案*/
	//讀檔案
	test_dwj02();
           

代碼:

#include <iostream>
#include<string>
#include "point.h"
#include "circle2.h"
#include <fstream>

using namespace std;


//建立全局變量
int global_a = 10;
int global_b = 10;

//const修飾的全局變量=全局常量
const int c_g_a = 10;
const int c_g_b = 10;



//棧區
int * func()
{
	int a = 10;//局部變量 存放在棧區,棧區的資料在函數執行完後自動釋放
	return &a;//傳回局部變量的位址
}

int * func2(int b)//形參資料也會放在棧區
{
	b = 100;
	int a = 10;//局部變量 存放在棧區,棧區的資料在函數執行完後自動釋放
	return &a;//傳回局部變量的位址
}

//堆區
int * func3()
{
	//利用new關鍵字,可以将資料開辟到堆區
	//指針 本質也是局部變量,放在棧區,但是指針儲存的資料是放在堆區
	int*p = new int(10);//new int(10)傳回整型資料10的位址
	return p;
}



//new
int * func4()
{
	//在堆區建立整型資料
	//new傳回的是該資料類型的指針
	//文法:new 資料類型(變量值),這是建立一個變量
	int *p_new = new int(10);
	return p_new;
}

void test01()
{
	int*p = func4();
	cout << *p << endl;//輸出10
	cout << *p << endl;//10
	cout << *p << endl;//10
	//堆區的資料 由程式員管理開辟,程式員管理釋放
	//如果想釋放堆區的資料,用delete釋放  文法:delete 指針變量名稱;
	delete p;
	//cout << *p << endl;//記憶體已經被釋放,再次通路就是非法操作,會報錯
}

//在堆區利用new開辟數組
void test02()
{
	//建立10個整型資料的數組,在堆區
	//文法: new 資料類型[資料值],這是建立資料值個變量
	int *arr = new int[10];//10代表數組有10個元素
	for (int i = 0; i < 10; i++)
	{
		arr[i] = i + 100;//給10個元素指派:100~109
	}
	for (int i = 0; i < 10; i++)
	{
		cout << arr[i] << endl;
	}
	//釋放數組
	//文法:delete[] 指針變量名;
	delete[]arr;
}




//引用做函數參數
//交換函數
//1.值傳遞
void mySwap01(int a, int b)
{
	int temp = a;
	a = b;
	b = temp;

	cout << "swap01\ta=" << a << "\tb=" << b << endl;//值傳遞形參發生改變
	return;
}

//2.位址傳遞
void mySwap02(int *a, int *b)
{
	int temp = *a;
	*a = *b;
	*b = temp;
	return;
}



//3.引用傳遞
void mySwap03(int &a,int &b)
{
	int temp = a;
	a = b;
	b = temp;
	return;
}



/*引用做函數傳回值*/
//文法:傳回值類型 & 函數名稱(參數清單) {函數體語句;return語句;}
//作用:引用是可以作為函數的傳回值存在的
//1.不要傳回局部變量的引用
int& test03()
{
	int a = 10;//局部變量存放在棧區
	return a;
}

//2.函數的調用可以作為左值
int& test04()
{
	static int a = 10;//靜态變量,存放在全局區,全局區上的資料在程式結束後系統釋放
	return a;
}



//引用的本質
//發現是引用,轉換為 int* const ref = &a; 
void func5(int& ref) 
{
	ref = 100;
	// ref是引用,轉換為*ref = 100 
	cout << "func5\tref=" << ref << endl;
} 


//列印資料函數
void showValue(int &val)
{
	val = 1000;
	cout << "value=" << val << endl;
}

//列印資料函數2
void showValue2(const int &val)
{
	//val = 1000;//錯誤,不可修改
	cout << "value=" << val << endl;
}



/*函數預設參數*/
int func6(int a, int b=20, int c=30)
{
	return a + b + c;
}

int func7(int a, int b=1, int c=2 )	//1.如果某個位置已經有了預設參數,那麼從這個位置往後都必須有預設值
{
	return a + b + c;
}

//2.如果函數聲明有預設參數,函數實作就不能有預設參數
//函數聲明和函數實作隻能有一個有預設參數,兩者任選一個給預設值就可以
//int func8(int a=10, int b=20);//函數聲明

int func8(int a=10, int b = 20)//函數實作
{
	return a + b ;
}


/*占位參數*/
//占位參數還可以有預設值 比如:void func9(int a,int =10)
void func9(int a,int)
{
	cout << "this is func9" << endl;
}



/*函數重載*/
//可以讓函數名相同,提高複用性
//函數重載滿足條件:
//1.同一個作用域下
//2.函數名稱相同
//3.函數參數類型不同或者個數不同或者順序不同

void funct()
{
	cout << "funct的調用" << endl;
}

void funct(int a)
{
	cout << "funct(int a)的調用" << endl;
}

void funct(double a)
{
	cout << "funct(double a)的調用" << endl;
}

void funct(int a, double b)
{
	cout << "funct(int a,double b)的調用:" << endl;
}

void funct(double a, int b)
{
	cout << "funct(double a, int b)的調用:" << endl;
}

//注意:函數的傳回值不可以作為函數重載的條件  int跟void傳回值不同,不可以作為重載的條件
//int funct(double a, int b)
//{
//	cout << "funct(double a, int b)的調用:" << endl;
//}


//函數重載注意事項
//1.引用作為重載條件
void fun(int &a)
{
	cout << "fun(int &a)調用" << endl;
}

void fun(const int &a)
{
	cout << "fun(const int &a)調用" << endl;
}
//2.函數重載碰到函數預設參數
void fun2(int a)
{
	cout << "fun2(int a)的調用" << endl;
}

void fun2(int a,int b=10)
{
	cout << "fun2(int a)的調用" << endl;
}



/*類和對象*/

/*封裝*/

//将屬性和行為作為一個整體,表現生活中的事物
//文法:class 類名 { 通路權限: 屬性;行為;};

//圓周率
const double PI = 3.14;

//設計一個圓類,求圓的周長
//圓的周長公式:2 * PI *半徑
//class 代表設計一個類,類後面緊跟着的是類名稱
class Circle
{
	//通路權限
public://公共權限

	//屬性
	int m_r;//半徑

	//行為,通常用函數來表示
	//擷取圓的周長
	double calculateZC()
	{
		return 2 * PI*m_r;
	}

};

//設計一個學生類,屬性有姓名和學号,可以給姓名和學号指派,可以顯示學生的姓名和學号
//設計學生類
class Student
{
	//公共權限
public:

	//屬性
	string m_Name;//姓名
	int m_ID;//學号

	//行為
	//顯示姓名和學号
	void showStudent()
	{
		cout << "姓名:"<< m_Name << "\t學号:" << m_ID << endl;
	}

	//給姓名指派
	void setName(string name)
	{
		m_Name = name;
	}

	//給學号指派
	void setID(int ID)
	{
		m_ID = ID;
	}
};


//将屬性和行為加以權限控制
	//public 公共權限   成員在類内可以通路,類外也可以通路
	//protected 保護權限   成員在類内可以通路,類外不可以通路  兒子也可以通路父親中的保護内容
	//private 私有權限   成員在類内可以通路,類外不可以通路   兒子不可以通路父親中的私有内容

class Person
{
public://公共權限
	//姓名
	string m_Name;

protected://保護權限
	//汽車
	string m_Car;

private://私有權限
	//銀行卡密碼
	int m_Password;

public:
	void func()
	{
		m_Name = "張三";
		m_Car = "拖拉機";
		m_Password = 123456;
	}

};



/*struct和class差別*/
	//struct預設權限為公共
	//class預設權限為私有
class C1
{
	int m_A;//預設權限是私有
};
struct C2
{
	int m_A;//預設權限是公共
};


/*成員屬性設定為私有*/
//優點1:将所有成員屬性設定為私有,可以自己控制讀寫權限
//優點2:對于寫權限,我們可以檢測資料的有效性

//設計人的類
class Person1
{
public:
	//寫姓名=設定姓名
	void setName(string name)
	{
		m_Name = name;
	}
	//讀姓名=擷取姓名
	string getName()
	{
		return m_Name;
	}
	//擷取年齡  可讀可寫  如果想修改(年齡的範圍必須是0-150之間)
	int getAge()
	{
		//m_Age = 0;//初始化為0歲
		return m_Age;
	}

	//設定年齡
	void setAge(int age)
	{
		if (age < 0 || age>150)
		{
			m_Age = 0;
			cout << "你這個老妖精!" << endl;
			return;//沒有return的話會往下執行m_Age=age指派操作,有return以後直接退出
		}
		m_Age = age;
	}

	//設定情人  隻寫
	void setLover(string lover)
	{
		m_Lover = lover;
	}

private:
	//姓名  可讀可寫
	string m_Name;
	//年齡  可讀可寫  如果想修改(年齡的範圍必須是0-150之間)
	int m_Age;
	//情人  隻寫
	string m_Lover;
};



/*設計長方體類*/
//1.建立長方體類
//2.設計屬性
//3.設計行為 擷取長方體面積和體積
//4.分别利用全局函數和成員函數 判斷兩個長方體是否相等
class Cube
{
public:
	//設定長
	void setL(int l)
	{
		m_L = l;
	}

	//擷取長
	int getL()
	{
		return m_L;
	}

	//設定寬
	void setW(int w)
	{
		m_W = w;
	}

	//擷取寬
	int getW()
	{
		return m_W;
	}

	//設定高
	void setH(int h)
	{
		m_H = h;
	}

	//擷取高
	int getH()
	{
		return m_H;
	}

	//擷取長方體面積
	int calculateS()
	{
		return 2 * m_L*m_H + 2 * m_H*m_W + 2 * m_W*m_L;
	}

	//擷取長方體體積
	int calculateV()
	{
		return m_H * m_L*m_W;
	}

	//利用成員函數判斷兩個長方體是否相等
	bool isSameByClass(Cube &cube)
	{
		//用getL()與cube.getL()判斷或者m_L與cube.getL()判斷
		if (getL() == cube.getL() && m_W == cube.getW() && m_H == cube.getH())
		{
			return true;
		}
		else
			return false;
	}

private:
	int m_L;//長
	int m_W;//寬
	int m_H;//高
};

//利用全局函數判斷兩個長方體是否相等
bool isSame(Cube &cube1, Cube &cube2)
{
	if (cube1.getL() == cube2.getL() && cube1.getW() == cube2.getW() && cube1.getH() == cube2.getH())
	{
		return true;
	}
	else
		return false;
}




/*點和圓的關系案例*/
//根據點到圓心的距離 與 半徑作比較來判斷

點類
//class Point
//{
//public:
//	//設定x
//	void setX(int x)
//	{
//		m_X = x;
//		//return;
//	}
//	//擷取x
//	int getX()
//	{
//		return m_X;
//	}
//	//設定y
//	void setY(int y)
//	{
//		m_Y = y;
//		//return;
//	}
//	//擷取y
//	int getY()
//	{
//		return m_Y;
//	}
//
//private:
//	int m_X;
//	int m_Y;
//};

圓類
//class Circle2
//{
//public:
//	//設定半徑
//	void setR(int r)
//	{
//		m_R = r;
//		//return;
//	}
//	//擷取半徑
//	int getR()
//	{
//		return m_R;
//	}
//	//設定圓心
//	void setCenter(Point center)
//	{
//		m_Center = center;
//		//return;
//	}
//	//擷取圓心
//	Point getCenter()
//	{
//		return m_Center;
//	}
//
//private:
//	int m_R;//半徑
//
//	//可以用一個點類來表示
//	//int m_X;//x坐标
//	//int m_Y;//y坐标
//	//圓心
//	//在類中可以讓另一個類作為本類的成員
//	Point m_Center;
//};

//判斷點和圓的關系
void isInCircle(Circle2 &c, Point &p)
{
	//計算兩點之間距離的平方
	int distance = 
		(c.getCenter().getX() - p.getX()) * (c.getCenter().getX()-p.getX()) 
		+ (c.getCenter().getY() - p.getY()) * (c.getCenter().getY() - p.getY());
	
	//計算半徑的平方
	int rDistance = c.getR() * c.getR();

	//判斷關系
	if (distance == rDistance)
	{
		cout << "點在圓上" << endl;
	}
	else if (distance > rDistance)
	{
		cout << "點在圓外" << endl;
	}
	else
	{
		cout << "點在圓内" << endl;
	}
}




/*對象的初始化和清理*/

//生活中我們買的電子産品都基本會有出廠設定,在某一天我們不用時候也會删除一些自己資訊資料保證安全 
//C++中的面向對象來源于生活,每個對象也都會有初始設定以及 對象銷毀前的清理資料的設定。
 


/*構造函數和析構函數*/
//一個對象或者變量沒有初始狀态,對其使用後果是未知
//同樣的使用完一個對象或變量,沒有及時清理,也會造成一定的安全問題

//c++利用了構造函數和析構函數解決上述問題,這兩個函數将會被編譯器自動調用,完成對象初始化和清理工作。
//對象的初始化和清理工作是編譯器強制要我們做的事情,是以如果我們不提供構造和析構,編譯器會提供
//編譯器提供的構造函數和析構函數是空實作。

//構造函數:主要作用在于建立對象時為對象的成員屬性指派,構造函數由編譯器自動調用,無須手動調用。 
//析構函數:主要作用在于對象銷毀前系統自動調用,執行一些清理工作。
 
//構造函數文法: 類名(){}
//1. 構造函數,沒有傳回值也不寫void 
//2. 函數名稱與類名相同 
//3. 構造函數可以有參數,是以可以發生重載 
//4. 程式在調用對象時候會自動調用構造,無須手動調用,而且隻會調用一次
 
//析構函數文法:  ~類名(){}
//1. 析構函數,沒有傳回值也不寫void 
//2. 函數名稱與類名相同,在名稱前加上符号 ~ 
//3. 析構函數不可以有參數,是以不可以發生重載 
//4. 程式在對象銷毀前會自動調用析構,無須手動調用,而且隻會調用一次
 
class Person_gz
{
public:
	//1.構造函數
	//沒有傳回值,不用寫void
	//函數名與類名相同
	//構造函數有參數,可以發生重載
	//建立對象的同時,構造函數會自動調用,而且隻調用一次
	Person_gz()
	{
		cout << "Person構造函數的調用" << endl;
	}

	//2.析構函數
	//沒有傳回值,不用寫void
	//函數名與類名相同,在名稱前加~
	//析構函數不可以有參數,不可以發生重載
	//對象在銷毀前會自動調用析構函數,而且隻會調用一次
	~Person_gz()
	{
		cout << "Person析構函數的調用" << endl;
	}

};

//構造和析構都是必須有的實作,如果我們自己不提供,編譯器會提供一個空實作的構造和析構函數
void test_gz()
{
	Person_gz person_gz; //在棧上的資料,test_gz執行完畢後,會釋放這個對象
}


/*構造函數的分類及調用*/
//按參數分為: 有參構造和無參構造
//按類型分為: 普通構造和拷貝構造
//三種調用方式:
//括号法
//顯示法
//隐式轉換法


//1.按照參數分類   無參構造(=預設構造)和有參構造
//2.按照類型分類   普通構造   和拷貝構造
class Person_fl
{
public:
	//構造函數
	Person_fl() //無參構造函數=預設構造函數
	{
		cout << "Person_fl的無參構造函數調用" << endl;
	}

	Person_fl(int a)//有參構造函數
	{
		age = a;
		cout << "Person_fl的有參構造函數調用" << endl;
	}

	//拷貝構造函數
	//拷貝函數文法: 類名(const 類名 & 引用名稱){ }
	Person_fl(const Person_fl &p) 
	{
		//将傳入的人身上的所有屬性,拷貝到我身上
		age = p.age;
		cout << "Person_fl的拷貝函數調用" << endl;
	}

	//析構函數
	~Person_fl()
	{
		cout << "Person_fl的析構函數調用" << endl;
	}

public:
	int age;
};

void test_fl()
{
	//1.括号法
	Person_fl person_fl1; //預設構造函數的調用,不要加()
	Person_fl person_fl2(10);//有參構造函數的調用
	Person_fl person_fl3(person_fl2); //拷貝構造函數的調用

	//注意事項
	//調用預設構造函數時候,不要加()
	//因為加了括号以後,編譯器會認為是一個函數的聲明,比如 Person p1();與void func();是以不會認為是在建立對象
	//Person_fl person_fl1(); //錯誤,不要加(),會認為是函數聲明

	cout << "person_fl2的年齡為:" << person_fl2.age << endl;
	cout << "person_fl3的年齡為" << person_fl3.age << endl;

	//2.顯示法
	Person_fl person_fl4;
	Person_fl person_fl5 = Person_fl(10);//有參構造, Person_fl(10)是匿名對象,目前行執行結束後,系統會立即回收掉匿名對象
	Person_fl person_fl6 = Person_fl(person_fl5);//拷貝構造

	//注意事項
	//不要利用拷貝函數來初始化匿名對象
	//Person_fl(person_fl5); //錯誤,編譯器會認為Person_fl(person_fl5);等價于Person_fl person_fl5;,編譯器會認為是對象聲明,會報重定義錯誤

	Person_fl(10);// Person_fl(10)是匿名對象,目前行執行結束後,系統會立即回收掉匿名對象即立即執行析構函數,執行完析構函數後再往下執行
	cout << "aaaa" << endl;

	//3.隐式轉換法
	Person_fl person_fl7 = 10;//相當于Person_fl person_fl7 = person_fl7(10);   有參構造
	Person_fl person_fl8 = person_fl7;//拷貝構造


}




/*拷貝構造函數調用時機*/
//C++中拷貝構造函數調用時機通常有三種情況
//使用一個已經建立完畢的對象來初始化一個新對象 
//值傳遞的方式給函數參數傳值 
//以值方式傳回局部對象

class Person_kb
{
public:
	Person_kb()
	{
		cout << "Person_kb預設構造函數調用" << endl;
	}

	~Person_kb()
	{
		cout << "Person_kb析構函數調用" << endl;
	}

	Person_kb(int age)
	{
		cout << "Person_kb有參構造函數調用" << endl;
		m_Age = age;
	}

	Person_kb(const Person_kb &p)
	{
		cout << "Person_kb拷貝構造函數調用" << endl;
		m_Age = p.m_Age;
	}


public:
	int m_Age;
};

//1.使用一個已經建立完畢的對象來初始化一個新對象
void test_kb1()
{
	Person_kb person_kb1(20);
	Person_kb person_kb2(person_kb1);

	cout << "person_kb2的年齡為:" << person_kb2.m_Age << endl;

	return;
}

//2.值傳遞的方式給函數參數傳值,值傳遞形參不會影響實參
void doWork(Person_kb p)
{
	return;
}

void test_kb2()
{
	Person_kb person_kb3;//無參構造函數
	doWork(person_kb3);//拷貝構造函數

	return;
}

//3.值方式傳回局部對象
Person_kb doWork2()
{
	Person_kb p1; //預設構造函數
	cout << (int*)&p1 << endl;

	return p1;
}

void test_kb3()
{
	Person_kb person_kb4 = doWork2(); //拷貝構造函數,相當于Person_kb person_kb4=p1的副本
	cout << (int*)&person_kb4 << endl;
	return;
}



/*構造函數調用規則*/
//預設情況下,c++編譯器至少給一個類添加3個函數
//1.預設構造函數(無參,函數體為空)
//2.預設析構函數(無參,函數體為空)
//3.預設拷貝構造函數,對屬性進行值拷貝

//構造函數調用規則如下:
//如果使用者定義有參構造函數,c++不在提供預設無參構造,但是會提供預設拷貝構造
//如果使用者定義拷貝構造函數,c++不會再提供其他構造函數

class Person_gzz
{
public:
	Person_gzz()
	{
		cout << "person_gzz的預設構造函數" << endl;
	}

	~Person_gzz()
	{
		cout << "Person_gzz的析構函數" << endl;
	}


	Person_gzz(int age)
	{
		cout << "Person_gzz的有參構造函數" << endl;
		m_Age = age;
	}
	/*Person_gzz(const Person_gzz &p)
	{
		cout << "Person_gzz的拷貝構造函數" << endl;
		m_Age = p.m_Age;
	}*/  //如果使用者沒有定義拷貝構造函數,編譯器會進行值拷貝m.Age=p.m.Age;

public:
	int m_Age;
};

void test_gzz()
{
	Person_gzz person_gzz1;//預設構造函數
	person_gzz1.m_Age = 18;

	Person_gzz person_gzz2(person_gzz1);//拷貝構造函數
	
	cout << "person_gzz2的年齡為:" << person_gzz2.m_Age << endl;
	return;
}

//如果使用者定義有參構造函數,c++不在提供預設無參構造,但是會提供預設拷貝構造
//如果使用者定義拷貝構造函數,c++不會再提供其他構造函數
void test_gzz2()
{
	Person_gzz person_gzz3(28);
	Person_gzz person_gzz4(person_gzz3);//編譯器提供預設的拷貝構造函數

	cout << "person_gzz4的年齡為:" << person_gzz4.m_Age << endl;

	return;
}




/*深拷貝與淺拷貝*/
//淺拷貝:簡單的指派拷貝操作(編譯器提供的拷貝構造函數)
//深拷貝:在堆區重新申請空間,進行拷貝操作
class Person_sq
{
public:
	Person_sq()
	{
		cout << "Person_sq的預設構造函數" << endl;
	}

	~Person_sq()
	{
		//析構代碼,将堆區開辟的資料做釋放操作
		//淺拷貝帶來的問題是堆區的記憶體重複釋放,會崩潰
		if (m_Height != NULL)
		{
			delete m_Height;
			m_Height = NULL;//置空操作,防止野指針出現
		}

		cout << "Person_sq的析構函數" << endl;
	}

	Person_sq(int age, int height)
	{
		m_Age = age;
		m_Height = new int(height);
		cout << "Person_sq的有參構造函數" << endl;
	}

	//自己實作拷貝構造函數,解決淺拷貝帶來的問題
	//如果屬性有在堆區開辟的,一定要自己提供拷貝構造函數,防止淺拷貝帶來的問題
	Person_sq(const Person_sq &p)
	{
		cout << "Person_sq拷貝構造函數的調用" << endl;
		m_Age = p.m_Age;
		//m_Height = p.m_Height;//編譯器預設實作的就是這行代碼
		//深拷貝操作

		m_Height = new int(*p.m_Height);//在堆區利用new重新開辟一個記憶體空間,讓m_Height重新指向高度值

	}

public:
	int m_Age;//年齡
	int *m_Height;//審稿
};

void test_sq1()
{
	Person_sq  person_sq1(200,180);

	cout << "person_sq1的年齡為:" << person_sq1.m_Age << "\tperson_sq1的身高為:" << *person_sq1.m_Height << endl;

	Person_sq person_sq2(person_sq1);//資料先進後出,是以person_sq2先被析構

	cout << "person_sq2的年齡為:" << person_sq2.m_Age << "\tperson_sq2的身高為:" << *person_sq2.m_Height << endl;
	return;
}



/*初始化清單*/
//作用:C++提供了初始化清單文法,用來初始化屬性
//文法:構造函數():屬性1(值1),屬性2(值2)...{}
//構造函數(變量類型1 變量名1,變量類型2 變量名2...):屬性1(變量名1),屬性2(變量名2)...{}
class Person_csh
{
public:
	傳統初始化操作
	//Person_csh(int a, int b, int c)
	//{
	//	m_A = a;
	//	m_B = b;
	//	m_C = c;
	//}

	//初始化清單來初始化屬性
	/*Person_csh() :m_A(10), m_B(20), m_C(30)
	{

	}*/
	Person_csh(int a, int b, int c) :m_A(a), m_B(b), m_C(c)
	{

	}

public:
	int m_A;
	int m_B;
	int m_C;
};

void test_csh()
{
	Person_csh person_csh(10, 20, 30);

	//Person_csh person_csh;

	cout << "m_A=" << person_csh.m_A << "\tm_B=" << person_csh.m_B << "\tm_C=" << person_csh.m_C << endl;

	return;
}



/*類對象作為類成員*/
//C++類中的成員可以是另一個類的對象,我們成該成員為對象成員
//當其他類對象作為本類成員,本類對象會先構造其他類對象再構造自身,即本例是先構造Phone,再構造Person
//先進後出,析構函數正好相反,即先析構自身再析構其他類

class A
{

};
class B
{
	A a;//執行個體化對象A
};
//B類中有對象A作為成員,A為對象成員

//手機類
class Phone
{
public:
	Phone(string pName)
	{
		m_Pname = pName;
		cout << "Phone的構造函數調用" << endl;
	}
	~Phone()
	{
		cout << "Phone的析構函數調用" << endl;
	}

public:
	//品牌名稱
	string m_Pname;
};

//人類
class Person_l
{
public:
	//隐式轉換法  m_Phone(pName)相當于 Phone m_Phone = pName
	Person_l(string name, string pName):m_Name(name),m_Phone(pName) //此時的m_Phone(pName)相當于 Phone m_Phone=pName
	{
		cout << "Person_l的構造函數調用" << endl;
	}
	~Person_l()
	{
		cout << "Person_l的析構函數調用" << endl;
	}

public:
	//姓名
	string m_Name;
	//手機
	Phone m_Phone;
};

void test_l()
{
	Person_l p("張三", "蘋果");

	cout << p.m_Name << "拿着" << p.m_Phone.m_Pname << "品牌的手機" << endl;
	return;
}




/*靜态成員*/
//靜态成員就是在成員變量和成員函數前加上關鍵字static,成為靜态成員
//靜态成員分類:
	//靜态成員變量
		//1.所有對象共用同一份資料
		//2.在編譯階段配置設定記憶體(全局區)
		//3.類内聲明,類外初始化
	//靜态成員函數
		//1.所有對象共享同一個函數
		//2.靜态成員函數隻能通路靜态成員變量

class Person_jt
{
public:

public:
	//所有對象都共享同一份資料
	//編譯階段就配置設定記憶體
	//類内聲明,類外初始化操作
	static int m_A;//類内聲明

	//靜态成員變量也是有通路權限的
private:
	static int m_B;

};
int Person_jt::m_A = 100;//類外初始化操作
int Person_jt::m_B = 300;

void test_jt()
{
	Person_jt p;
	//100
	cout << p.m_A << endl;

	Person_jt p2;
	p2.m_A = 200;//所有對象共享同一份資料
	//200
	cout << p.m_A << endl;
}

void test_jt2()
{
	//靜态成員變量  不屬于某個對象上 所有對象都共享同一份資料
	//是以靜态成員變量有兩種通路方式

	//1.通過對象進行通路
	Person_jt p;
	cout << "通過對象進行通路:" << endl;
	cout << p.m_A << endl;

	//2.通過類名進行通路
	cout << "通過類名進行通路:" << endl;
	cout << Person_jt::m_A << endl;
	//cout << Person_jt::m_B << endl; //錯誤,私有權限通路不到

}

//靜态成員函數
//所有對象共享同一個函數
//靜态成員函數隻能通路靜态成員變量
class Person_j
{
public:
	static void func()
	{
		m_A = 1000;//靜态成員函數可以通路靜态成員變量
		//m_B = 200;//錯誤,靜态成員函數不可以通路非靜态成員變量,因為無法區分到底是哪個對象的m_B屬性
		cout << "static void func調用" << endl;
	}

public:
	static int m_A;//靜态成員變量
	int m_B;//非靜态成員變量

	//靜态成員函數也是有通路權限的
private:
	static void func2()
	{
		cout << "static void func2函數調用" << endl;
	}

};
int Person_j::m_A = 0;


//有兩種通路方式
void test_j()
{
	//1.通過對象通路
	Person_j p;
	p.func();

	//2.通過類名通路
	Person_j::func();
	//Person_j::func2();  //錯誤,類外無法通路私有靜态成員函數
}





/*C++對象模型和this指針*/

/*成員變量和成員函數分開存儲*/
//1.在C++中,類内的成員變量和成員函數分開存儲
//2.隻有非靜态成員變量才屬于類的對象上

class Person_zz
{
public:
	void func()
	{

	}//非靜态成員函數不屬于類的對象
	static void func2()
	{

	}//靜态成員函數不屬于類的對象

public:
	int m_A;//非靜态成員變量屬于類的對象
	static int m_B;//靜态成員變量不屬于類的對象
};
int Person_zz::m_B = 0;//靜态成員變量類内聲明類外初始化

void test_zz1()
{
	Person_zz p;
	//空對象占用記憶體空間為:1
	//C++編譯器會給每個空對象也配置設定一個位元組空間,是為了區分空對象占用記憶體的位置
	//每個空對象也應該有一個獨一無二的記憶體位址
	cout << "zize of p=" << sizeof(p) << endl;
}

void test_zz2()
{
	Person_zz p;
	cout << "zize of p=" << sizeof(p) << endl;
	return;
}




/*this指針概念*/
//每一個非靜态成員函數隻會誕生一份函數執行個體,也就是說多個同類型的對象會共用一塊代碼
//問題是:這一塊代碼是如何區分哪個對象調用自己的呢?
//C++通過提供特殊的對象指針-this指針,解決上述問題
//this指針指向被調用的成員函數所屬的對象
//this指針是隐含每一個非靜态成員函數内的一種指針
//this指針不需要定義,直接使用即可

//this指針的用途:
	//1.當形參和成員變量同名時,可用this指針來區分
	//2.在類的非靜态成員函數中傳回對象本身,可使用return *this;

class Person_this
{
public:
	Person_this(int age)
	{
		//this指針指向被調用的成員函數所屬的對象
		//本例是:this指向被調用的Person_this成員函數所屬的對象p1,即this指向p1
		//Person_this::age = age;正确,用這樣也對,說明age是Person_this作用域下的
		this->age = age;
	}

	Person_this& PersonAddAge(Person_this &p)  //傳回p2的本體要用引用的方式 Person_this &,傳回值類型一定要用引用
		//如果傳回值類型是Person_this相當于是拷貝函數,會另外複制一份新的資料,輸出将一直為20,無法實作累加
	{
		this->age += p.age;//自身的年齡=自身的年齡+p的年齡
		//this指向p2的指針,*this指向的就是p2這個對象的本體
		return *this;
	}
public:
	int age;
};

//1.解決名稱沖突
void test_this01()
{
	Person_this p1(18);

	cout << "p1的年齡為:" << p1.age << endl;
}

//2.傳回對象本身用*this
void test_this02()
{
	Person_this p1(10);
	Person_this p2(10);

	p2.PersonAddAge(p1).PersonAddAge(p1).PersonAddAge(p1);

	cout << "p2的年齡為:" << p2.age << endl;
}




/*空指針通路成員函數*/
//C++中空指針也可以調用成員函數的,但是也要注意有沒有用到this指針
//如果用到this指針,需要加以判斷保證代碼的健壯性

class Person_k
{
public:
	void showClassName()
	{
		cout << "this is Person class" << endl;
		return;
	}

	void showPersonAge()
	{
		//報錯,因為傳入的指針是NULL空指針,無法通路屬性

		//糾正如下:
		if (this == NULL)
		{
			return;
		}//提高代碼的健壯性

		cout << "Person's age is:" << m_Age << endl;//此時的m_Age相當于this->m_Age,但是此時是空指針,無法通路其中的屬性,是無中生有
		return;
	}

public:
	int m_Age;
};

void test_k01()
{
	Person_k *p = NULL;//空指針
	p->showClassName();//通過空指針調用成員函數
	p->showPersonAge();
	return;
}




/*const修飾成員函數*/
//常函數:(隻讀狀态)
	//1.成員函數後加const後稱這個函數為常函數  文法:傳回值類型 函數名() const  {}
	//2.常函數内不可以修改成員屬性
	//3.成員屬性聲明時加關鍵字mutable後,在常函數中依然可以修改

//常對象:
	//1.聲明對象前加const稱該對象為常對象
	//2.常對象隻能調用常函數

class Person_c
{
public:
	//this指針的本質是指針常量,相當于Person * const this;   指針的指向是不可以修改的
	//如果想讓指針指向的值也不可以修改:  const Person * const this;  對應常函數如下:
	//在成員函數後面加const,修飾的是this指針,讓指針指向的值也不可以修改
	void showPerson() const//相當于 const Person * const this;
	{
		this->m_B = 100;
		//m_A = 100;//m_A=100;相當于this->m_A=100;
		//this = NULL;//錯誤,this指針不可以修改指針的指向
		return;
	}

	void func()
	{
		return;
	}
public:
	int m_A;
	mutable int m_B;//特殊變量,即使在常函數中也可以修改這個值,加關鍵字mutable
};

//常對象
void test_c()
{
	const Person_c p;//在對象前加const變為常對象
	//常對象也不允許修改指針指向的值
	//p.m_A = 100;
	p.m_B = 100;//m_B是特殊值,在常對象下也可以修改

	//常對象隻能調用常函數
	p.showPerson();
	//p.func();//常對象不可以調用普通成員函數,因為普通成員函數可以修改屬性
	return;
}




/*友元*/
//在程式裡,有些私有屬性也想讓類外特殊的一些函數或者類進行通路,就需要用到友元的技術
//友元的目的就是讓一個函數或者類通路另一個類中的私有成員
//友元的關鍵字為 friend
//友元的三種實作
	//1.全局函數做友元
	//2.類做友元
	//3.成員函數做友元

//全局函數做友元
//建築物類
class Building_y
{
	//goodGay全局函數是Building的好朋友,可以通路Building中的私有屬性
	//文法: friend 全局函數;  相當于   friend 傳回值類型 函數名(參數清單);
	friend void goodGay(Building_y *building);

public:
	Building_y()
	{
		m_SittingRoom = "客廳";
		m_BedRoom = "卧室";

		return;
	}

public:
	string m_SittingRoom;//客廳

private:
	string m_BedRoom;//卧室
};

//全局函數
void goodGay(Building_y *building)//指針傳遞
{
	cout << "好基友的全局函數正在通路:" << building->m_SittingRoom << endl;
	cout << "好基友的全局函數正在通路:" << building->m_BedRoom << endl;
	
	return;
}

void test_y01()
{
	Building_y building;
	goodGay(&building);//通過&把位址傳進去
	return;
}

//類做友元
//文法:friend class 類名;
class Building_yy;//告訴編譯器一會兒我會寫一個Building類,先别給我報錯
class GoodGay
{
public:
	void visit();//參觀函數  通路Building中的屬性
	GoodGay();
private:
	Building_yy * building;
};

class Building_yy
{
	friend class GoodGay;//GoodGay類是本類的好朋友,可以通路本類的私有成員
public:
	Building_yy();
public:
	string m_SettingRoom;//客廳
private:
	string m_BedRoom;//卧室
};
//類外寫成員函數
Building_yy::Building_yy()//加上作用域,告訴編譯器是那個作用下的構造函數
{
	m_SettingRoom = "客廳";
	m_BedRoom = "卧室";
}
GoodGay::GoodGay()
{
	//建立建築物對象
	building = new Building_yy;
}
void GoodGay::visit()
{
	cout << "好基友類正在通路:" << building->m_SettingRoom << endl;
	cout << "好基友類正在通路:" << building->m_BedRoom << endl;
}
void test_yy01()
{
	GoodGay gg;
	gg.visit();
	return;
}

//成員函數做友元
//先聲明Building類,再寫GoodGAY類,最後寫Building類,GoodGAY::visit要寫到類外。否則會報錯
//如果在GoodGAY類内寫visit函數會報錯,因為此時Building并未定義成員變量等,無法調用Building裡的成員變量
//程式自上向下執行,聲明定義順序别弄錯,visit函數要類外實作
class Building;
class GoodGAY
{
public:
	GoodGAY();
	void visit_yy01();//讓visit_yy01函數可以通路Building中私有成員
	void visit_yy02();//讓visit_yy02函數不可以通路Building中私有成員

public:
	Building *building;
};

class Building
{
	//告訴編譯器,GoodGAY類下的visit_yy01成員函數作為本類的好朋友,可以通路私有成員
	friend void GoodGAY::visit_yy01();
public:
	Building();
public:
	string m_SettingRoom;//客廳
private:
	string m_BedRoom;//卧室 
};
//類外實作成員函數
Building::Building()
{
	m_SettingRoom = "客廳";
	m_BedRoom = "卧室";
}
GoodGAY::GoodGAY()
{
	building = new Building;//用new在堆區建立一個指針,并用building來維護這個對象
}
void GoodGAY::visit_yy01()
{
	cout << "visit_yy01函數正在通路:" << building->m_SettingRoom << endl;
	cout << "visit_yy01函數正在通路:" << building->m_BedRoom << endl;

}
void GoodGAY::visit_yy02()
{
	cout << "visit_yy02函數正在通路:" << building->m_SettingRoom << endl;
	//cout << "visit_yy02函數正在通路:" << building->m_BedRoom << endl;

}
void test_yy()
{
	GoodGAY gg;
	gg.visit_yy01();
	gg.visit_yy02();
}




/*運算符重載*/
//運算符重載:對已有的運算符重新進行定義,賦予其另一種功能,以适應不同的資料類型

/*加号運算符重載*/
//對于内置的資料類型,編譯器知道如何進行運算
//作用:實作兩個自定義資料類型相加的運算

class Person_jh
{
public:
	1.成員函數重載+号
	文法: 傳回值類型 operator+(參數清單){};
	//Person_jh operator+(Person_jh &p)
	//{
	//	Person_jh temp;
	//	temp.m_A = this->m_A + p.m_A;
	//	temp.m_B = this->m_B + p.m_B;
	//	return temp;
	//}

public:
	int m_A;
	int m_B;
};

//2.全局函數重載+号

Person_jh operator+(Person_jh &p1, Person_jh &p2)
{
	Person_jh temp;
	temp.m_A = p1.m_A + p2.m_A;
	temp.m_B = p1.m_B + p2.m_B;
	return temp;
}

//函數重載的版本
Person_jh operator+(Person_jh &p, int num)
{
	Person_jh temp;
	temp.m_A = p.m_A + num;
	temp.m_B = p.m_B + num;
	return temp;
}



//注意順序,一定要先定義重載加号再調用
void test_jh01()
{
	Person_jh p1;
	p1.m_A = 10;
	p1.m_B = 10;
	Person_jh p2;
	p2.m_A = 10;
	p2.m_B = 10;

	//成員函數重載本質調用
	//Person_jh p3 = p1.operator+(p2);
	//全局函數重載本質調用
	//Person_jh p3 = operator+(p1, p2);
	//兩種方法都可以簡化為:
	Person_jh p3 = p1 + p2;

	//運算符重載也可以發生函數重載
	Person_jh p4 = p1 + 100;//Person + int

	cout << "p3.m_A=" << p3.m_A << endl;
	cout << "p3.m_B=" << p3.m_B << endl;

	cout << "p4.m_A=" << p4.m_A << endl;
	cout << "p4.m_B=" << p4.m_B << endl;

	return;
}
//總結:
//1.對于内置的資料類型的表達式的運算符是不可能改變的
//2.不要濫用運算符重載




/*左移運算符重載*/
//文法:傳回值類型 operator<<(參數清單) {} ;
//作用:可以輸出自定義的資料類型

class Person_zy
{
	friend ostream& operator<<(ostream &cout, Person_zy &p);
	friend void test_zy01();
public:

	Person_zy(int a, int b)
	{
		m_A = a;
		m_B = b;
	}

	//利用成員函數來重載左移運算符
	//p.operator<<(cout) 簡化為: p<<cout
	//不會利用成員函數來重載<<運算符,因為無法實作cout在左側
	//void operator<<(Person_zy &p)
	//{

	//	return;
	//}

private:
	int m_A;
	int m_B;

};

//隻能利用全局函數重載左移運算符
//cout屬于輸出流ostream,是以用ostream &cout,通過引用傳進來
ostream& operator<<(ostream &cout, Person_zy &p)
//本質是operator<<(cout,p)  簡化為:cout<<p
//傳回值引用可以了解為可以利用該位址進行下一步操作,也就是可以繼續<<接着輸出其他(鍊式)。
{
	cout << "m_A=" << p.m_A << "\tm_B=" << p.m_B << endl;

	return cout;
}

void test_zy01()
{
	Person_zy p(10, 10);

	cout << p.m_A << "\t" << p.m_B << endl;
	cout << p << "Hello world" << endl;
	return;
}

//總結
//重載左移運算符配合友元可以輸出自定義資料類型




/*遞增運算符重載*/
//作用:通過重載遞增運算符實作自己的整型資料

//自定義整型
class MyInteger
{
	friend ostream& operator<<(ostream& cout, const MyInteger& my);
public:
	MyInteger()
	{
		m_Num = 0;
	}

	//重載前置++運算符,傳回引用是為了一直對一個資料進行遞增操作
	MyInteger& operator++()
	{
		//先進行++運算
		m_Num++;

		//再将自身做傳回,this指向自身,*this就是把自身解引用
		return *this;
	}

	//重載後置++運算符
	//傳回值類型不同不可以作為區分函數的條件
	//後置遞增傳回值
	MyInteger operator++(int) //這個int代表占位參數,用于區分前置和後置遞增
	{
		//先記錄當時結果
		MyInteger temp = *this;

		//後遞增
		m_Num++;

		//最後将記錄結果做傳回
		return temp;
	}


private:
	int m_Num;

};

//重載左移運算符
ostream& operator<<(ostream& cout, const MyInteger& my)
//上面加了const是因為test_zy02()裡的cout<<會報錯。
{
	cout << my.m_Num ;
	return cout;

}

void test_my01()
{
	MyInteger myint;
	cout << ++(++myint) << endl;
	cout << myint << endl;
}

void test_my02()
{
	MyInteger myint;
	cout << myint++ << endl;
	cout << myint << endl;

	return;
}

//總結:前置遞增傳回引用,後置遞增傳回值



/*指派運算符重載*/
//c++編譯器至少給一個類添加4個函數
	//1. 預設構造函數(無參,函數體為空) 
	//2. 預設析構函數(無參,函數體為空) 
	//3. 預設拷貝構造函數,對屬性進行值拷貝 
	//4. 指派運算符 operator=, 對屬性進行值拷貝
//如果類中有屬性指向堆區,做指派操作時也會出現深淺拷貝問題

class Person_fz
{
public:
	Person_fz(int age)
	{
		m_Age = new int(age);//利用new把資料開辟到堆區

	}

	~Person_fz()
	{
		if (m_Age != NULL)
		{
			delete m_Age;
			m_Age = NULL;
		}

	}

	//重載指派運算符
	Person_fz& operator=(Person_fz &p)
	{
		//編譯器是提供淺拷貝m_Age=p.m_Age;
		//應該先判斷是否有屬性在堆區,如果有先釋放幹淨,然後再深拷貝
		if (m_Age != NULL)
		{
			delete m_Age;
			m_Age = NULL;
		}
		//深拷貝
		m_Age = new int(*p.m_Age);

		//傳回對象本身
		return *this;//this是指向自身的指針,*this就是解引用,也就是自身
	}

public:
	int *m_Age;
};

void test_fz01()
{
	Person_fz p1(18);
	Person_fz p2(20);
	Person_fz p3(30);
	p3 = p2 = p1;//指派操作

	cout << "p1的年齡:" << *p1.m_Age << endl;//把m_Age指針解引用
	cout << "p2的年齡:" << *p2.m_Age << endl;//把m_Age指針解引用
	cout << "p3的年齡:" << *p3.m_Age << endl;//把m_Age指針解引用

	return;
}




/*關系運算符重載*/
//作用:重載關系運算符,可以讓兩個自定義類型對象進行對比操作

class Person_gx
{
public:
	Person_gx(string name, int age)
	{
		m_Name = name;
		m_Age = age;
	}

	//重載==
	bool operator==(Person_gx &p)
	{
		if (this->m_Age == p.m_Age&&this->m_Name == p.m_Name)
		{
			return true;
		}
		else
			return false;

	}

	bool operator!=(Person_gx &p)
	{
		if (this->m_Age != p.m_Age || this->m_Name != p.m_Name)
		{
			return true;
		}
		else
			return false;
	}

public:
	string m_Name;
	int m_Age;
};

void test_gx01()
{
	Person_gx p1("張三", 18);
	Person_gx p2("張三", 18);

	if (p1 != p2)
	{
		cout << "p1!=p2" << endl;
	}
	else
	{
		cout << "p1==p2" << endl;

	}
	
}




/*函數調用運算符重載*/
//函數調用運算符{}也可以重載
//由于重載後使用的方式非常像函數的調用,是以稱為仿函數
//仿函數沒有固定寫法,非常靈活

class Myprint
{
public:

	//重載函數調用運算符
	void operator()(string test)
	{
		cout << test << endl;
	}
public:

};

void test_dy02(string test)
{
	cout << test << endl;
}


void test_dy01()
{
	Myprint my;
	my("Hello world!");//重載函數調用運算符,由于使用起來非常類似于函數調用,又稱為仿函數
	test_dy02("Hello world");
}

//仿函數非常靈活,沒有固定的寫法
//加法類
class MyAdd
{
public:
	int operator()(int num1, int num2)
	{
		return num1 + num2;
	}

public:

};

void test_dy03()
{
	MyAdd my;
	int result = my(100, 200);
	cout << "result=" << result << endl;

	//匿名函數對象
	//類名()是建立一個匿名對象,比如MyAdd()
	//後面的(200,300)是重載
	//匿名對象是在目前行執行完時立即被釋放
	cout << MyAdd()(200, 300) << endl;

}




/*繼承*/
//下級别的成員除了擁有上一級的共性,還有自己的特性

/*繼承的基本文法*/

//普通實作
//Java頁面
class Java
{
public:

	void header()
	{
		cout << "首頁、公開課、登入、注冊。。。(公共頭部)" << endl;
	}

	void footer()
	{
		cout << "幫助中心、交流合作、站内地圖。。。(公共底部)" << endl;
	}

	void left()
	{
		cout << "Java\Python\C++(公共分類清單)" << endl;
	}

	void content()
	{
		cout << "Java學科視訊" << endl;
	}


public:

};

//Python頁面
class Python
{
public:

	void header()
	{
		cout << "首頁、公開課、登入、注冊。。。(公共頭部)" << endl;
	}

	void footer()
	{
		cout << "幫助中心、交流合作、站内地圖。。。(公共底部)" << endl;
	}

	void left()
	{
		cout << "Java\Python\C++(公共分類清單)" << endl;
	}

	void content()
	{
		cout << "Python學科視訊" << endl;
	}


public:

};

//C++
//Python頁面
class CPP
{
public:

	void header()
	{
		cout << "首頁、公開課、登入、注冊。。。(公共頭部)" << endl;
	}

	void footer()
	{
		cout << "幫助中心、交流合作、站内地圖。。。(公共底部)" << endl;
	}

	void left()
	{
		cout << "Java\Python\C++(公共分類清單)" << endl;
	}

	void content()
	{
		cout << "C++學科視訊" << endl;
	}


public:

};


void test_jc01()
{
	cout << "Java下載下傳視訊的頁面如下:" << endl;
	Java java;
	java.header();
	java.footer();
	java.left();
	java.content();

	cout << "-------------------------" << endl;

	cout << "Python下載下傳視訊的頁面如下:" << endl;
	Python python;
	python.header();
	python.footer();
	python.left();
	python.content();

	cout << "-------------------------" << endl;

	cout << "C++下載下傳視訊的頁面如下:" << endl;
	CPP cpp;
	cpp.header();
	cpp.footer();
	cpp.left();
	cpp.content();

}

//繼承實作
//繼承的好處:減少重複代碼
//文法:class 子類: 繼承方式 父類
//子類也稱為派生類
//父類也稱為基類

//派生類中的成員包括兩部分
	//1.從基類繼承過來的
	//2.自己增加的成員
//從基類繼承過來的表現其共性,新增的成員展現其個性

//公共頁面類
class BasePage
{
public:
	void header()
	{
		cout << "首頁、公開課、登入、注冊。。。(公共頭部)" << endl;
	}

	void footer()
	{
		cout << "幫助中心、交流合作、站内地圖。。。(公共底部)" << endl;
	}

	void left()
	{
		cout << "Java\Python\C++(公共分類清單)" << endl;
	}

public:

};

//Java頁面
//文法:  class 類名:public 公共類名
class Java_jc:public BasePage
{
public:
	void content()
	{
		cout << "Java學科視訊" << endl;
	}

public:

};

//Python頁面
class Python_jc :public BasePage
{
public:
	void content()
	{
		cout << "Python學科視訊" << endl;
	}

public:

};

//C++頁面
class CPP_jc :public BasePage
{
public:
	void content()
	{
		cout << "C++學科視訊" << endl;
	}

public:

};

void test_jc02()
{
	cout << "Java的下載下傳視訊的頁面如下:" << endl;
	Java_jc java;
	java.header();
	java.footer();
	java.left();
	java.content();

	cout << "-------------------------" << endl;

	cout << "Python的下載下傳視訊的頁面如下:" << endl;
	Python_jc python;
	python.header();
	python.footer();
	python.left();
	python.content();

	cout << "-------------------------" << endl;

	cout << "C++的下載下傳視訊的頁面如下:" << endl;
	CPP_jc cpp;
	cpp.header();
	cpp.footer();
	cpp.left();
	cpp.content();

}





/*繼承方式*/
//繼承的文法:class 子類:繼承方式 父類
//繼承方式有三種:
	//1.公共繼承 父類中的public還是public,父類的protected還是protected,父類的private不可以通路
	//2.保護繼承 父類中的public和protected都是protected,弗雷德private不可以通路
	//3.私有繼承 父類中的public和protected都是private,父類的private不可以通路

//公共繼承
class Base1
{
public:
	int m_A;
protected:
	int m_B;
private:
	int m_C;
};

class Son1 :public Base1
{
public:
	void func()
	{
		m_A = 10;//父類中的public成員到子類中依然是public,public是類内可以通路,類外也可以通路
		m_B = 20;//父類中的protect成員到子類中依然是protected
		//m_C = 10;//錯誤,父類中的private成員到子類中不可以通路
	}
};

void test_jc03()
{
	Son1 son;
	son.m_A = 100;
	//son.m_B = 100;//m_B是protected,隻能類内通路,類外不能通路
}

//保護繼承
class Base2
{
public:
	int m_A;
protected:
	int m_B;
private:
	int m_C;

};

class Son2 :protected Base2
{
public:
	void func()
	{
		m_A = 100;//父類中的public到子類中變為protected
		m_B = 100;//父類中的protected到子類中仍未protected
		//m_C = 100;//錯誤,父類的private,子類通路不到
	}
};

void test_jc04()
{
	Son2 son;
	//son.m_A = 1000;//錯誤,在Son2中m_A變為protected,是以類外通路不到
	//son.m_B = 1000;//錯誤,protected類外通路不到,隻能類内通路
}

//私有繼承
class Base3
{
public:
	int m_A;
protected:
	int m_B;
private:
	int m_C;

};

class Son3 :private Base3
{
public:
	void func()
	{
		m_A = 100;//父類中public成員到子類中變為private成員
		m_B = 100;//父類中protected成員到子類中變為private成員
		//m_C = 100;//父類中private成員,子類無法通路
	}
};

void test_jc05()
{
	Son3 son;
	//son.m_A = 10;//錯誤,到Son3中變為private,類外通路不到,隻有類内能通路到
	//son.m_B = 10;//錯誤,到Son3中變為private,類外通路不到,隻有類内能通路到
}

class GrandSon3 :public Son3
{
public:
	void func()
	{
		//m_A = 1000;//錯誤,到Son3中m_A為private,通路不到
		//m_B = 100;//錯誤,到Son3中m_A為private,通路不到
	}
};




/*繼承中的對象模型*/
class Base_mx
{
public:
	int m_A;
protected:
	int m_B;
private:
	int m_C;
};

class Son_mx :public Base_mx
{
public:
	int m_D;
};

void test_mx()
{
	//父類中所有非靜态成員屬性都會被子類繼承下去
	//父類中私有成員屬性是被編譯器隐藏了,是以是通路不到的,但是确實被繼承下去了
	cout << "size of Son_mx=" << sizeof(Son_mx) << endl;//結果為16=4*4
}
//利用開發人員指令提示工具檢視對象模型
//1.跳轉盤符 D:
//2.跳轉檔案路徑: cd 檔案路徑
//3.檢視指令:cl /d1 reportSingleClassLayout類名 檔案名




/*繼承中構造和析構順序*/
//子類繼承父類後,當建立子類對象時也會調用父類的構造函數
class Base_xg
{
public:
	Base_xg()
	{
		cout << "Base的構造函數" << endl;
	}

	~Base_xg()
	{
		cout << "Base的析構函數" << endl;
	}
};

class Son_xg :public Base_xg
{
public:
	Son_xg()
	{
		cout << "Son的構造函數" << endl;
	}

	~Son_xg()
	{
		cout << "Son的析構函數" << endl;
	}
};

void test_xg()
{
	//Base_xg b;
	Son_xg s;
}
//先構造父類,再構造子類
//析構的順序與構造的順序相反
//現有父類的構造函數,再有子類的構造函數,再有子類的析構函數,最後是父類的析構函數




/*繼承同名成員處理方式*/
//通路子類同名成員直接通路即可
//通路父類同名成員需要加作用域

class Base_tm
{
public:
	Base_tm()
	{
		m_A = 100;
	}

	void func()
	{
		cout << "Base下的func()調用" << endl;
	}

	void func(int a)
	{
		cout << "Base下的func(int a)調用" << endl;
	}

public:
	int m_A;
};

class Son_tm :public Base_tm
{
public:
	Son_tm()
	{
		m_A = 200;
	}

	void func()
	{
		cout << "Son下的func()調用" << endl;
	}

public:
	int m_A;
};

//同名成員屬性處理
void test_tm01()
{
	Son_tm s;
	cout << "Son下的m_A=" << s.m_A << endl;
	//如果通過子類對象通路到父類中的同名成員,需要加作用域
	cout << "Base下的m_A=" << s.Base_tm::m_A << endl;

}

//同名成員函數處理
void test_tm02()
{
	Son_tm s;
	s.func();//直接調用,調用的是子類中的同名成語
	s.Base_tm::func();//調用父類中的同名成員函數時需要加作用域
	//如果子類中出現和父類同名的成員函數,子類的同名成員函數會隐藏掉父類中所有的同名成員函數,隻要同一個函數名字的就算,不管有參無參
	//s.func(100);//錯誤
	//如果想通路到父類中被隐藏的同名成員函數,需要加作用域
	s.Base_tm::func(100);
}
//總結:
	//1. 子類對象可以直接通路到子類中同名成員 
	//2. 子類對象加作用域可以通路到父類同名成員 
	//3. 當子類與父類擁有同名的成員函數,子類會隐藏父類中同名成員函數,加作用域可以通路到父類中同名函數




/*繼承同名靜态成員處理方式*/
//靜态成員和費靜态成員出現同名時的處理方式一緻
	//1.通路子類同名成員  直接通路即可
	//2.通路父類同名成員  需要加作用域

class Base_jt
{
public:
	static void func()
	{
		cout << "Base下的static void func()" << endl;
	}

	static void func(int a)
	{
		cout << "Base下的static void func(int a)" << endl;
	}

public:
	static int m_A;//靜态成員變量類内聲明,類外初始化
};
int Base_jt::m_A = 100;//靜态成員變量類内聲明,類外初始化

class Son_jt :public Base_jt
{
public:
	static void func()
	{
		cout << "Son下的static void func()" << endl;
	}

public:
	static int m_A;//靜态成員變量類内聲明,類外初始化
};
int Son_jt::m_A = 200;//靜态成員變量類内聲明,類外初始化

//同名靜态成員屬性
void test_jt01()
{
	//1.通過對象通路
	Son_jt s;
	cout << "Son下的m_A=" << s.m_A << endl;
	cout << "Base下的m_A=" << s.Base_jt::m_A << endl;

	//2.通過類名通路
	cout << "Son下的m_A=" << Son_jt::m_A << endl;
	//第一個::代表通過類名方式通路,第二個::代表通路父類作用域下
	cout << "Base下的m_A=" << Son_jt::Base_jt::m_A << endl;
}

//同名靜态成員函數
void test_jt02()
{
	//1.通過對象通路
	Son_jt s;
	s.func();
	s.Base_jt::func();

	//2.通過類名通路
	Son_jt::func();
	Son_jt::Base_jt::func();
	//子類出現和父類同名靜态成員函數,也會隐藏父類中所有同名成員函數
	//如果想通路父類中被隐藏同名成員,需要加作用域
	Son_jt::Base_jt::func(100);
}
//總結:
//同名靜态成員處理方式和非靜态處理方式一樣,隻不過有兩種通路的方式(通過對象 和 通過類名)





/*多繼承文法*/
//C++允許一個類繼承多個類
//文法:class 子類:繼承方式 父類1,繼承方式 父類2...
//多繼承可能會引發父類中有同名成員出現,需要加作用域區分
//C++實際開發中不建議使用

class Base_d1
{
public:
	Base_d1()
	{
		m_A = 100;
	}

public:
	int m_A;
};

class Base_d2
{
public:
	Base_d2()
	{
		m_A = 200;
	}

public:
	int m_A;
};

//子類  需要繼承Base1和Base2
class Son_d :public Base_d1, public Base_d2
{
public:
	Son_d()
	{
		m_C = 300;
		m_D = 400;
	}

public:
	int m_C;
	int m_D;
};

void test_d01()
{
	Son_d s;
	cout << "size of Son=" << sizeof(s) << endl;//16
	//當父類中出現同名成員,需要加作用域區分
	cout << "Base::m_A=" << s.Base_d1::m_A << endl;
	cout << "Base::m_A=" << s.Base_d2::m_A << endl;

}
//總結: 
//多繼承中如果父類中出現了同名情況,子類使用時候要加作用域




/*菱形繼承*/
//菱形繼承概念
	//兩個派生類繼承同一個基類
	//又有某個類同時繼承兩個派生類
	//這種繼承稱為菱形繼承或者鑽石繼承
//菱形繼承問題:
//1. 羊繼承了動物的資料,駝同樣繼承了動物的資料,當草泥馬使用資料時,就會産生二義性。 
//2. 草泥馬繼承自動物的資料繼承了兩份,其實我們應該清楚,這份資料我們隻需要一份就可以。

//動物類
class Animal
{
public:

public:
	int m_Age;
};

//利用虛繼承可以解決菱形繼承的問題
//在繼承之前加上關鍵字virtual變為虛繼承
//文法:class 子類名:virtual 繼承方式 父類名
//Animal類稱為虛基類
//羊類
class Sheep :virtual public Animal
{

};

//駝類
class Tuo :virtual public Animal
{

};

//羊駝類
class SheepTuo :public Sheep, public Tuo
{

};

void test_lx01()
{
	SheepTuo st;
	st.Sheep::m_Age = 18;
	st.Tuo::m_Age = 28;
	//當菱形繼承,兩個父類擁有相同資料,需要加作用域來區分
	cout << "st.Sheep::m_Age=" << st.Sheep::m_Age << endl;
	cout << "st.Tuo::m_Age=" << st.Tuo::m_Age << endl;

	//這份資料我們知道 隻要有一份就可以了,菱形繼承導緻資料有兩份,浪費資源
	cout << "st.m_Age=" << st.m_Age << endl;
	//vbptr代表虛基類指針,指向VBtable虛基類表  v-virtual  b-base ptr-pointer
}
//總結:
//菱形繼承帶來的主要問題是子類繼承兩份相同的資料,導緻資源浪費以及毫無意義 
//利用虛繼承可以解決菱形繼承問題




/*多态*/

/*多态的基本概念*/
//多态分為兩類
	//1.靜态多态:函數重載和運算符重載都屬于靜态多态,複用函數名
	//2.動态多态:派生類和虛函數實作運作時多态

//靜态多态和動态多态的差別:
	//1.靜态多态的函數位址早綁定,編譯階段确定函數位址
	//2.動态多态的函數位址晚綁定,運作階段确定函數位址

//動物類
class Animal_dt
{
public:
	//虛函數:virtual 傳回類型 函數名(參數清單){};
	virtual void speak()
	{
		cout << "動物在說話" << endl;
	}
public:

};

//貓類
class Cat_dt :public Animal_dt
{
public:
	//重寫  函數傳回值類型、函數名、參數清單完全相同
	virtual void speak()
	{
		cout << "小貓在說話" << endl;
	}
public:

};

//狗類
class Dog_dt :public Animal_dt
{
public:
	virtual void speak()
	{
		cout << "小狗在說話" << endl;
	}
};

//執行說話的函數
//位址早綁定,在編譯階段确定函數位址
//如果想執行讓貓說話,這個函數位址就不能提前綁定,需要在運作階段進行綁定,位址晚綁定

//動态多台滿足條件
//1.有繼承關系
//2.子類要重寫父類的虛函數,是重寫不是重載。(父類加virtual,子類加不加virtual都可以)

//動态多态的使用
//1.父類的指針或者引用指向子類對象

void doSpeak(Animal_dt &animal)//Animal_dt &animal =cat,父類的引用或者指針可以直接指向子類對象
{
	animal.speak();
}

void test_dt01()
{
	Cat_dt cat;
	doSpeak(cat);

	Dog_dt dog;
	doSpeak(dog);

}

void test_dt02()
{
	cout << "size of Animal=" << sizeof(Animal_dt) << endl;//Animal裡不加virtual代表空類,輸出1
}

//vfptr虛函數(表)指針,指向虛函數表
//v-virtual f-function ptr-pointer
//vftalble虛函數表,表内部記錄虛函數的位址
//v-virtual f-function table-table




/*案例:電腦類*/
//案例描述:分别利用普通寫法和多态技術,設計實作兩個操作數進行運算的電腦類
//多态的優點:
	//1.代碼組織結構清晰
	//2.可讀性強
	//3.利于前期和後期的擴充以及維護

//普通寫法
class Calculator
{
public:
	int getResult(string oper)
	{
		if (oper == "+")
		{
			return m_Num1 + m_Num2;
		}
		else if (oper == "-")
		{
			return m_Num1 - m_Num2;
		}
		else if (oper == "*")
		{
			return m_Num1 * m_Num2;
		}
		//如果想擴充新的功能,需要修改源碼
		//在真實的開發中,提倡開閉原則:對擴充進行開發,對修改進行關閉

	}

public:
	int m_Num1;//操作數1
	int m_Num2;//操作數2
};

void test_jsq01()
{
	//建立電腦對象
	Calculator c;
	c.m_Num1 = 10;
	c.m_Num2 = 10;

	cout << c.m_Num1 << "+" << c.m_Num2 << "=" << c.getResult("+") << endl;
	cout << c.m_Num1 << "-" << c.m_Num2 << "=" << c.getResult("-") << endl;
	cout << c.m_Num1 << "*" << c.m_Num2 << "=" << c.getResult("*") << endl;

}

//利用多态實作電腦
//多态的好處:
	//1.組織結構清晰
	//2.可讀性強
	//3.對于前期和後期擴充以及維護友善

//實作電腦抽象類
class AbstractCalculator
{
public:
	virtual int getResult()
	{

		return 0;
	}
public:
	int m_Num1;
	int m_Num2;
};

//加法電腦類
class AddCalculator :public AbstractCalculator
{
public:
	int getResult()//子類重寫父類中的虛函數(父類加virtual,子類加不加都可以)
	{
		return m_Num1 + m_Num2;
	}
public:

};

//減法電腦類
class SubCalculator :public AbstractCalculator
{
public:
	int getResult()
	{
		return m_Num1 - m_Num2;
	}
public:

};

//乘法電腦
class MulCalculator :public AbstractCalculator
{
public:
	int getResult()
	{
		return m_Num1 * m_Num2;
	}
public:

};

void test_jsq02()
{
	//多态使用條件
	//父類指針或者引用指向子類對象
	//加法運算
	AbstractCalculator *abc = new AddCalculator;//開辟堆區,需要手動開辟手動釋放
	abc->m_Num1 = 100;
	abc->m_Num2 = 100;

	cout << abc->m_Num1 << "+" << abc->m_Num2 << "=" << abc->getResult() << endl;
	//用完記得銷毀,隻是資料銷毀了,資料類型還未變
	delete abc;//delete後abc的資料類型仍然為指針

	//減法電腦
	abc = new SubCalculator;
	abc->m_Num1 = 100;
	abc->m_Num2 = 100;

	cout << abc->m_Num1 << "-" << abc->m_Num2 << "=" << abc->getResult() << endl;
	delete abc;

	//乘法電腦
	abc = new MulCalculator;
	abc->m_Num1 = 100;
	abc->m_Num2 = 100;

	cout << abc->m_Num1 << "*" << abc->m_Num2 << "=" << abc->getResult() << endl;
	delete abc;


}
//總結:C++開發提倡利用多态設計程式架構,因為多态優點很多
 




/*純虛函數和抽象類*/
//在多态中,通常父類中虛函數的實作是毫無意義的,主要都是調用子類重寫的内容
//是以可以将虛函數改為純虛函數
//純虛函數文法:virtual 傳回值類型 函數名(參數清單)=0;
//當類中有了純虛函數,這個類也稱為抽象類
//抽象類特點:
	//1.無法執行個體化對象
	//2.子類必須重寫抽象類中的純虛函數,否則也屬于抽象類

class Base_cx
{
public:
	virtual void func() = 0;//純虛函數,隻要有一個純虛函數,這個類稱為抽象類

public:

};

//子類
class Son_cx :public Base_cx
{
public:
	virtual void func()
	{
		cout << "func調用" << endl;
	}
public:

};



void test_cx01()
{
	//Base_cx b;//錯誤,抽象類無法執行個體化對象
	//new Base_cx;//錯誤,堆區也無法建立執行個體化對象
	//Son_cx s; //錯誤,子類必須重寫抽象類中的純虛函數,否則也屬于抽象類
	Son_cx s; //正确,子類中已經重寫了抽象類中的純虛函數,可以進行執行個體化對象了
	Base_cx *b = new Son_cx;
	b->func();
	delete b;
}




/*案例:制作飲品*/
//案例描述:制作流程:煮水、沖泡、倒入杯中、加入輔料
//利用多态技術實作本案例,提供抽象制作飲品基類,提供子類制作咖啡和茶葉

class AbstractDrining
{
public:
	//煮水
	virtual void Boil() = 0;

	//沖泡
	virtual void Brew() = 0;

	//倒入杯中
	virtual void PourInCup() = 0;

	//加入輔助佐料
	virtual void PutSomething() = 0;

	//制作飲品
	void makeDrinking()
	{
		Boil();
		Brew();
		PourInCup();
		PutSomething();
	}

public:

};

//制作咖啡
class Coffee :public AbstractDrining
{
public:
	//煮水
	virtual void Boil()
	{
		cout << "煮農夫山泉" << endl;
	}

	//沖泡
	virtual void Brew()
	{
		cout << "沖泡咖啡" << endl;
	}

	//倒入杯中
	virtual void PourInCup()
	{
		cout << "倒入咖啡杯中" << endl;
	}

	//加入輔助佐料
	virtual void PutSomething()
	{
		cout << "加入糖和牛奶" << endl;
	}

public:

};

//制作茶葉
class Tea :public AbstractDrining
{
public:
	//煮水
	virtual void Boil()
	{
		cout << "煮礦泉水" << endl;
	}

	//沖泡
	virtual void Brew()
	{
		cout << "沖泡茶葉" << endl;
	}

	//倒入杯中
	virtual void PourInCup()
	{
		cout << "倒入茶杯中" << endl;
	}

	//加入輔助佐料
	virtual void PutSomething()
	{
		cout << "加入枸杞" << endl;
	}

public:

};

//制作函數
void doWork_al02(AbstractDrining *abs)//AbstractDrining *abs=new Coffee;
{
	abs->makeDrinking();
	delete abs;//手動開辟,手動釋放
}

void test_al02()
{
	//制作咖啡
	doWork_al02(new Coffee);//父類的指針指向子類的對象

	//制作茶
	doWork_al02(new Tea);
}




/*虛析構和純虛析構*/
//多态使用時,如果子類中有屬性開辟到堆區,那麼父類指針在釋放時無法調用到子類的析構代碼
//解決方式:将父類中的析構函數改為虛析構或者純虛析構

//虛析構和純虛析構共性:
	//1.可以解決父類指針釋放子類對象
	//2.都需要有具體的函數實作

//虛析構和純虛析構差別:
	//如果是純虛析構,該類屬于抽象類,無法執行個體化對象

//文法:
//虛析構文法:virtual ~類名(){};
//純虛析構文法:virtual ~類名()=0;  在類外實作  類名::~類名(){};

class Animal_x
{
public:
	
	Animal_x()
	{
		cout << "Animal的構造函數調用" << endl;
	}
	
	//純虛函數
	virtual void speak() = 0;

	//虛析構和純虛析構隻能有一個

	//利用虛析構可以解決父類指針釋放子類對象時不幹淨的問題
	//virtual ~Animal_x()//父類中析構函數前加上virtual改為虛析構,便會進行子類的析構
	//{
	//	cout << "Animal的析構函數調用" << endl;
	//}

	//純虛析構,需要聲明也需要實作
	//有了純虛析構之後,這個類屬于抽象類,無法執行個體化對象
	virtual ~Animal_x() = 0;


public:

};

//純虛析構也需要代碼的實作
Animal_x::~Animal_x()
{
	cout << "Animal的純虛析構函數調用" << endl;

}


class Cat_x :public Animal_x
{
public:
	Cat_x(string name)
	{
		cout << "Cat的構造函數調用" << endl;
		m_Name = new string(name);
	}
	
	virtual void speak()
	{
		cout << *m_Name << "貓在說話" << endl;
	}

	~Cat_x()
	{
		if (m_Name != NULL)
		{
			cout << "Cat析構函數調用" << endl;
			delete m_Name;
			m_Name = NULL;
		}
	}

public:
	string *m_Name;
};

void test_x01()
{
	Animal_x *animal = new Cat_x("Tom");
	animal->speak();
	//父類的指針在析構時候,不會調用子類中的析構函數,導緻子類如果有堆區屬性的話會出現記憶體洩漏
	delete animal;
}

//總結:
//1. 虛析構或純虛析構就是用來解決通過父類指針釋放子類對象
//2. 如果子類中沒有堆區資料,可以不寫為虛析構或純虛析構
//3. 擁有純虛析構函數的類也屬于抽象類,無法執行個體化對象




/*案例:電腦組裝*/
//電腦主要組成部件為 CPU(用于計算),顯示卡(用于顯示),記憶體條(用于存儲)
//将每個零件封裝出抽象基類,并且提供不同的廠商生産不同的零件,例如Intel廠商和Lenovo廠商
//建立電腦類提供讓電腦工作的函數,并且調用每個零件工作的接口
//測試時組裝三台不同的電腦進行工作

//抽象不同零件類
//抽象CPU類
class CPU
{
public:
	//抽象的計算函數
	virtual void calculate() = 0;

public:

};

//抽象顯示卡類
class VideoCard
{
public:
	//抽象的顯示函數
	virtual void display() = 0;

public:

};

//抽象記憶體條類
class Memory
{
public:
	//抽象的存儲函數
	virtual void storage() = 0;

public:

};

//電腦類
class Computer
{
public:
	Computer(CPU *cpu, VideoCard *vc, Memory *mem)
	{
		m_cpu = cpu;
		m_vc = vc;
		m_mem = mem;
	}

	//提供工作的函數
	void work()
	{
		//讓零件工作起來,調用接口
		m_cpu->calculate();
		m_vc->display();
		m_mem->storage();
	}

	//提供析構函數,釋放三個電腦零件
	Computer()
	{
		//釋放CPU零件
		if (m_cpu != NULL)
		{
			delete m_cpu;
			m_cpu = NULL;
		}

		//釋放顯示卡零件
		if (m_vc != NULL)
		{
			delete m_vc;
			m_vc = NULL;
		}

		//釋放記憶體條零件
		if (m_mem != NULL)
		{
			delete m_mem;
			m_mem = NULL;
		}
	}

private:
	CPU *m_cpu;//CPU零件的指針
	VideoCard *m_vc;//顯示卡零件的指針
	Memory *m_mem;//記憶體條零件的指針
};

//具體廠商
//Intel廠商
class IntelCPU :public CPU
{
	virtual void calculate()
	{
		cout << "Intel的CPU開始計算了!" << endl;
	}

};

class IntelVideoCard :public VideoCard
{
	virtual void display()
	{
		cout << "Intel的顯示卡開始顯示了!" << endl;
	}

};

class IntelMemory :public Memory
{
	virtual void storage()
	{
		cout << "Intel的記憶體條開始存儲了!" << endl;
	}

};

//Lenovo廠商
class LenovoCPU :public CPU
{
	virtual void calculate()
	{
		cout << "Lenovo的CPU開始計算了!" << endl;
	}

};

class LenovoVideoCard :public VideoCard
{
	virtual void display()
	{
		cout << "Lenovo的顯示卡開始顯示了!" << endl;
	}

};

class LenovoMemory :public Memory
{
	virtual void storage()
	{
		cout << "Lenovo的記憶體條開始存儲了!" << endl;
	}

};

void test_dn()
{
	//第一台電腦零件
	CPU *intelcpu = new IntelCPU;
	VideoCard *intelvc = new IntelVideoCard;
	Memory *intelmem = new IntelMemory;

	//建立第一台電腦
	Computer *computer1 = new Computer(intelcpu, intelvc, intelmem);
	computer1->work();
	delete computer1;
	cout << "--------------------" << endl;
	//建立第二台電腦
	Computer *computer2 = new Computer(new LenovoCPU, new LenovoVideoCard, new LenovoMemory);
	computer2->work();
	delete computer2;
	cout << "--------------------" << endl;

	//建立第三台電腦
	Computer *computer3 = new Computer(new IntelCPU, new LenovoVideoCard, new IntelMemory);
	computer3->work();
	delete computer3;
	cout << "--------------------" << endl;


}




/*檔案操作*/
//程式運作時産生的資料都屬于臨時資料,程式一旦運作結束都會被釋放
//通過檔案可以将資料持久化
//C++中對檔案操作需要包含頭檔案<fstream>//檔案流

//檔案類型分為兩種:
	//1.文本檔案:文本以文本的ASCII碼形式存儲在計算機中
	//2.二進制檔案:檔案以文本的二進制形式存儲在計算機中,使用者一般不能直接讀懂它們

//操作檔案的三大類:
	//1.ofstream:寫操作  o-output f-file
	//2.ifstream:讀操作  i-input
	//3.fstream:讀寫操作



/*文本檔案*/
//寫檔案

//1.包含頭檔案#include<fstream>
//2.建立流對象 ofstream ofs;
//3.打開檔案  ofs.open("檔案路徑",打開方式);
//4.寫資料 ofs<<"寫入的資料";
//5.關閉檔案  ofs.close();

//檔案打開方式:
//ios::in 為讀檔案而打開檔案
//ios::out 為寫檔案而打開檔案
//ios::ate 初始位置:檔案尾
//ios::app 追加方式寫檔案
//ios::trunc 如果檔案存在先删除,再建立
//ios::binary 二進制方式
//注意:檔案打開方式可以配合使用,利用 | 操作符
//例如:用二進制方式寫檔案 ios::binary | ios::out

void test_xwj01()
{
	ofstream ofs;
	ofs.open("test.txt", ios::out);
	ofs << "姓名:張三" << endl;
	ofs << "性别:男" << endl;
	ofs << "年齡:18" << endl;
	ofs.close();
}
//總結:
//檔案操作必須包含頭檔案 fstream 
//讀檔案可以利用 ofstream ,或者fstream類 
//打開檔案時候需要指定操作檔案的路徑,以及打開方式 
//利用 << 可以向檔案中寫資料 
//操作完畢,要關閉檔案

//讀檔案
//1.包含頭檔案 #include<fstream>
//2.建立流對象 ifstream ifs;
//3.打開檔案并判斷檔案是否打開成功  ifs.open(“檔案路徑”,打開方式); 通過ifs.is_open()來判斷
//4.讀資料   四種方式讀取
//5.關閉檔案  ifs.close();

void test_dwj01()
{
	ifstream ifs;
	ifs.open("test.txt",ios::in);
	if (!ifs.is_open())
	{
		cout << "檔案打開失敗" << endl;
		return;
	}

	//讀資料
	第一種
	//char buf[1024] = { 0 };
	//while (ifs >> buf)//一行一行的讀,讀到頭以後傳回一個false
	//{
	//	cout << buf << endl;
	//}

	第二種
	//char buf[1024] = { 0 };
	//while (ifs.getline(buf, sizeof(buf)))
	//{
	//	cout << buf << endl;
	//}

	第三種
	//string buf;
	//while (getline(ifs, buf))
	//{
	//	cout << buf << endl;
	//}

	//第四種,不推薦用
	char c;
	while ((c = ifs.get()) != EOF)//EOF是end of file檔案尾,看是否讀到檔案尾
	{
		cout << c;
	}

	ifs.close();
}
//總結:
//讀檔案可以利用 ifstream ,或者fstream類 
//利用is_open函數可以判斷檔案是否打開成功 
//close 關閉檔案



/*二進制檔案*/
//以二進制的方式對檔案進行讀寫操作
//打開方式要指定為:ios::binary

//寫檔案
//二進制方式寫檔案主要利用流對象調用成員函數write
//函數原型ostream& write(const char * buffer,int len);
//參數解釋:字元指針buffer指向記憶體中一段存儲空間。len是讀寫的位元組數
 
class Person_xwj
{
public:

public:
	char m_Name[64];//姓名    不要用string,可能會出現問題,要用char
	int m_Age;//年齡
};

void test_xwj02()
{
	ofstream ofs;
	ofs.open("person.txt", ios::out | ios::binary);
	Person_xwj p = { "張三",18 };
	ofs.write((const char *)&p, sizeof(Person_xwj));
	ofs.close();
}
//總結:
//檔案輸出流對象 可以通過write函數,以二進制方式寫資料

//讀檔案
//二進制方式讀檔案主要利用流對象調用成員函數read
//函數原型:istream& read(char *buffer, int len);
//參數解釋:字元指針buffer指向記憶體中一段存儲空間。len是讀寫的位元組數

class Person_dwj
{
public:
	char m_Name[64];
	int m_Age;
};

void test_dwj02()
{
	ifstream ifs;
	ifs.open("person.txt", ios::in | ios::binary);
	if (!ifs.is_open())
	{
		cout << "檔案打開失敗" << endl;
		return;
	}

	Person_dwj p;
	ifs.read((char*)&p, sizeof(Person_dwj));
	cout << "姓名:" << p.m_Name << "\t年齡:" << p.m_Age << endl;
	ifs.close();


}


int main()
{
	/*二進制檔案*/
	//寫檔案
	test_xwj02();
	//讀檔案
	test_dwj02();


	
	
	/*文本檔案*/
	//寫檔案
	test_xwj01();
	test_dwj01();
	



	/*案例:電腦組裝*/
	test_dn();
	
	
	
	/*虛析構和純虛析構*/
	test_x01();
	
	
	/*案例:制作飲品*/
	test_al02();
	
	
	
	/*純虛函數和抽象類*/
	test_cx01();
	
	
	
	/*案例:電腦類*/
	test_jsq01();
	test_jsq02();
	
	
	/*多态的基本概念*/
	test_dt01();
	test_dt02();


	
	
	/*菱形繼承*/
	test_lx01();
	
	
	
	/*多繼承文法*/
	test_d01();
	
	
	
	/*繼承同名靜态成員處理方式*/
	//test_jt01();
	test_jt02();
	
	
	
	/*繼承同名成員處理方式*/
	test_tm01();
	test_tm02();
	
	
	/*繼承中構造和析構順序*/
	test_xg();
	

	
	/*繼承中的對象模型*/
	test_mx();
	
	
	
	/*繼承方式*/

	

	/*繼承*/
	//下級别的成員除了擁有上一級的共性,還有自己的特性

	/*繼承的基本文法*/
	test_jc01();
	test_jc02();
	
	
	
	
	/*函數調用運算符重載*/
	test_dy01();
	test_dy03();
	
	
	/*關系運算符重載*/
	test_gx01();
	
	
	
	
	
	/*指派運算符重載*/
	test_fz01();
	int a = 10;
	int b = 20;
	int c = 30;

	c = b = a;
	cout << "a=" << a << endl;
	cout << "b=" << b << endl;
	cout << "c=" << c << endl;

	
	
	
	/*遞增運算符重載*/
	//test_my01();
	test_my02();



	/*左移運算符重載*/
	test_zy01();
	
	
	
	
	
	/*加号運算符重載*/
	test_jh01();


	
	
	
	/*友元*/
	//全局函數做友元
	test_y01();
	//類做友元
	test_yy01();
	//成員函數做友元
	test_yy();
	
	
	/*const修飾成員函數*/
	test_c();
	
	
	
	
	/*空指針通路成員函數*/
	test_k01();
	
	
	
	
	/*this指針概念*/
	test_this01();
	test_this02();
	
	
	
	
	
	/*C++對象模型和this指針*/

	/*成員變量和成員函數分開存儲*/
	//test_zz1();
	test_zz2();




	/*靜态成員*/
	//test_jt();
	test_jt2();
	
	test_j();


	
	
	/*類對象作為類成員*/
	test_l();
	
	

	
	/*初始化清單*/
	test_csh();
	
	
	
	
	
	/*深拷貝與淺拷貝*/
	test_sq1();
	
	
	
	
	/*構造函數調用規則*/
	test_gzz();
	test_gzz2();
	
	
	
	/*拷貝構造函數調用時機*/
	test_kb1();
	test_kb2();
	test_kb3();
	
	
	
	/*構造函數的分類及調用*/
	test_fl();
	
	
	
	/*對象的初始化和清理*/

	//生活中我們買的電子産品都基本會有出廠設定,在某一天我們不用時候也會删除一些自己資訊資料保證安全 
	//C++中的面向對象來源于生活,每個對象也都會有初始設定以及 對象銷毀前的清理資料的設定。


	/*構造函數和析構函數*/
	test_gz();//建立對象的時候,構造函數會自動調用,不需要手動調用,而且調用隻有一次
	
	Person_gz person_gz2; //此時并不會釋放,是以不會調用析構函數
	
	
	
	/*判斷點和圓的關系*/
	
	//建立圓
	Circle2 circle2;
	circle2.setR(10);
	Point center;
	center.setX(10);
	center.setY(0);
	circle2.setCenter(center);

	//建立點
	Point point;
	point.setX(10);
	point.setY(10);

	//判斷點和圓的關系
	isInCircle(circle2, point);




	/*設計長方體類*/
	//執行個體化對象  建立長方體
	Cube cube1;
	cube1.setL(10);
	cube1.setH(10);
	cube1.setW(10);

	cout << "cube1的面積為:" << cube1.calculateS() << endl;
	cout << "cube1的體積為:" << cube1.calculateV() << endl;
	
	//建立第二個長方體
	Cube cube2;
	cube2.setL(10);
	cube2.setH(10);
	cube2.setW(10);

	//判斷兩個長方體是否相等
	//利用全局函數判斷
	bool result1 = isSame(cube1, cube2);
	if (result1)
	{
		cout << "兩個相等" << endl;
	}
	else
	{
		cout << "兩個不相等" << endl;
	}

	//利用成員函數判斷
	bool result2 = cube1.isSameByClass(cube2);
	if (result2)
	{
		cout << "兩個相等	" << endl;
	}
	else
	{
		cout << "兩個不相等	" << endl;
	}

	
	/*成員屬性設定為私有*/
	//優點1:将所有成員屬性設定為私有,可以自己控制讀寫權限
	//優點2:對于寫權限,我們可以檢測資料的有效性
	Person1 pp1;
	pp1.setName("張六");
	cout << "姓名為:" << pp1.getName() << endl;
	pp1.setAge(1000);
	cout << "年齡為:" << pp1.getAge() << endl;
	pp1.setLover("王五");
	
	/*struct和class差別*/
	//struct預設權限為公共
	//class預設權限為私有
	C1 cc1;
	//cc1.m_A = 100;//錯誤,class預設權限為私有,類外不可以通路
	C2 c2;
	c2.m_A = 100;//正确,struct預設權限為公共
	
	
	

	/*類和對象*/
	//c++面向對象的三大特性為:封裝、繼承、多态
	//C++認為萬事萬物都皆為對象,對象上有其屬性和行為
	//例如:
		//人可以作為對象,屬性有姓名、年齡、身高、體重...,行為有走、跑、跳、吃飯、唱歌...
		//車也可以作為對象,屬性有輪胎、方向盤、車燈..., 行為有載人、放音樂、放空調...
	//具有相同性質的 == 對象 == ,我們可以抽象稱為 == 類 == ,人屬于人類,車屬于車類
	
	
	/*封裝*/
	//封裝的意義:
		//将屬性和行為作為一個整體,表現生活中的事物
		//将屬性和行為加以權限控制
	

	//将屬性和行為作為一個整體,表現生活中的事物
	//文法:class 類名 { 通路權限: 屬性;行為;};
	//類中的屬性和成員,統一稱為成員
	//屬性=成員屬性=成員變量
	//行為=成員函數=成員方法

	//設計一個圓類,求圓的周長
	//圓的周長公式:2 * PI *半徑
	
	//執行個體化:通過一個類建立一個對象的過程
	//通過圓類建立具體的圓(對象)
	//文法:類名稱 對象名稱;
	Circle c1;
	//給圓對象的屬性進行指派
	c1.m_r = 10;

	cout << "圓的周長為:" << c1.calculateZC() << endl;
	
	
	//設計一個學生類,屬性有姓名和學号,可以給姓名和學号指派,可以顯示學生的姓名和學号
	//建立一個具體的學生   執行個體化對象
	Student s1;
	//給s1對象進行屬性指派操作
	s1.m_Name = "張三";
	s1.m_ID = 1;
	//顯示學生資訊
	s1.showStudent();
	
	Student s2;
	s2.setName("李四");
	s2.setID(2);
	s2.showStudent();
	
	//将屬性和行為加以權限控制
	//public 公共權限
	//protected 保護權限
	//private 私有權限
	
	//執行個體化具體對象
	Person p1;
	p1.m_Name = "王五";
	//p1.m_Car = "奔馳"; //錯誤,保護權限内容,在類外通路不到
	//p1.m_Password = 123;//錯誤,私有權限内容,類外通路不到



	
	/*記憶體分區模型*/

	//記憶體大方向分為四個區域
	//代碼區:存放函數體的二進制代碼,由作業系統進行管理
	//全局區:存放全局變量和靜态變量以及常量
	//棧區:由編譯器自動配置設定釋放,存放函數的參數值,局部變量等
	//堆區:由程式員配置設定和釋放,若程式員不釋放,程式結束時由作業系統回收



	/*程式運作之前:代碼區,全局區*/

	//代碼區:存放CPU執行的機器指令
	//代碼區是共享的,共享的目的是對于頻繁被執行的程式,隻需要在記憶體中有一份代碼即可
	//代碼區是隻讀的,使其隻讀的原因是防止程式意外地修改它的指令

	//全局區包括:全局變量、靜态變量、全局常量、字元串常量
	//全局區的資料在程式結束後由作業系統釋放

	//建立普通局部變量
	int a1 = 10;
	int b1 = 10;

	cout << "局部變量a1的位址為:" << (int)&a1 << endl;
	cout << "局部變量b1的位址為:" << (int)&b1 << endl;

	//輸出定義的全局變量
	cout << "全局變量global_a的位址為:" << (int)&global_a << endl;
	cout << "全局變量global_b的位址為:" << (int)&global_b << endl;

	//建立靜态變量,在普通變量前加static就屬于靜态變量
	//文法:static 資料類型 變量名稱;
	static int static_a = 10;
	static int static_b = 10;

	cout << "靜态變量static_a的位址為:" << (int)&static_a << endl;
	cout << "靜态變量static_b的位址為:" << (int)&static_b << endl;

	//常量
	//字元串常量  文法:"..."
	cout << "字元串常量的位址為:" << (int)&"hello world" << endl;

	//const修飾的變量:修飾全局變量、修飾局部變量
	//const修飾的全局變量
	cout << "全局常量c_g_a的位址為:" << (int)&c_g_a << endl;
	cout << "全局常量c_g_b的位址為:" << (int)&c_g_b << endl;

	//const修飾的局部變量
	//c-const,g-global,l-local
	const int c_l_a = 10;
	const int c_l_b = 10;

	cout << "局部常量c_l_a的位址為:" << (int)&c_l_a << endl;
	cout << "局部常量c_l_b的位址為:" << (int)&c_l_b << endl;
	//全局常量、全局變量、靜态變量和字元串常量放在全局區,局部常量和局部變量不在全局區

	//總結
	//C++中在程式運作前分為全局區和代碼區 
	//代碼區特點是共享和隻讀 
	//全局區中存放全局變量、靜态變量、常量 
	//常量區中存放const修飾的全局常量和字元串常量



	/*程式運作後:棧區和堆區*/
	//棧區:由編譯器自動配置設定釋放,存放函數的參數值,局部變量等
	//注意事項:不要傳回局部變量的位址,棧區開辟的資料由編譯器自動釋放
	
	//接收func函數的傳回值
	int *p = func();

	cout << *p << endl;//輸出10 //第一次可以列印正确的數字,是因為編譯器做了一次保留
	cout << *p << endl;//輸出亂碼 //第二次這個資料就不再保留

	//堆區;由程式員配置設定釋放,若程式員不釋放,程式結束時由作業系統回收
	//在C++中主要利用new在堆區開辟記憶體
	int *p_dq = func3();
	
	cout << *p_dq << endl;//輸出10
	cout << *p_dq << endl;//輸出10



	/*new操作符*/
	//利用new在堆區開辟資料
	//文法:new 資料類型
	//利用new建立的資料,會傳回該資料對應類型的指針
	//堆區開辟的資料,由程式員手動開辟,手動釋放,釋放利用delete進行
	test01();
	test02();



	/*引用*/
	//引用:給變量起别名
	//文法:資料類型 &别名=原名;
	int a_yy = 10;
	int &b_yy = a_yy;
	
	cout << "a=" << a_yy << endl;//輸出10
	cout << "b=" << b_yy << endl;//10

	b_yy = 100;//通過修改b_yy,此時a_yy、b_yy都修改為100

	cout << "a=" << a_yy << endl;//輸出100
	cout << "b=" << b_yy << endl;//100

	//引用注意事項
	//引用必須初始化
	int a_zy = 10;
	//int &b_zy; //錯誤,必須要初始化
	int &b_zy = a_zy;//一旦初始化後,就不可以更改

	cout << "a=" << a_zy << endl;//輸出10
	cout << "b=" << b_zy << endl;//輸出10

	//引用在初始化後,不可以改變
	int c_zy = 20;
	b_zy = c_zy;//指派操作,而不是更改引用
	//把b_zy指派了20,此時a_zy和b_zy都是20

	cout << "a=" << a_zy << endl;//輸出20
	cout << "b=" << b_zy << endl;//輸出20
	cout << "c=" << c_zy << endl;//輸出20


	/*引用做函數參數*/
	//作用:函數傳參時,可以利用引用的技術讓形參修飾實參
	//優點:可以簡化指針修改實參
	int a_yyhs = 10;
	int b_yyhs = 20;

	cout << "交換前:" << endl;
	cout << "a_yyhs=" << a_yyhs << "\tb_yyhs=" << b_yyhs << endl;

	mySwap01(a_yyhs, b_yyhs);//值傳遞,形參不會修飾實參,main函數仍然輸出原來的值
	cout << "值傳遞交換後:" << endl;
	cout << "a_yyhs=" << a_yyhs << "\tb_yyhs=" << b_yyhs << endl;

	mySwap02(&a_yyhs, &b_yyhs);//位址傳遞,形參會修飾實參
	cout << "位址傳遞交換後:" << endl;
	cout << "a_yyhs=" << a_yyhs << "\tb_yyhs=" << b_yyhs << endl;

	mySwap03(a_yyhs, b_yyhs);//引用傳遞,形參會修飾實參
	cout << "引用傳遞交換後:" << endl;
	cout << "a_yyhs=" << a_yyhs << "\tb_yyhs=" << b_yyhs << endl;
	//通過引用參數産生的效果同按位址傳遞是一樣的。引用的文法更清楚簡單



	/*引用做函數傳回值*/
	//作用:引用是可以作為函數的傳回值存在的
	//1.不要傳回局部變量的引用
	int &ref = test03();

	cout << "ref=" << ref << endl;//第一次正确,輸出10,是因為編譯器做了保留
	cout << "ref=" << ref << endl;//第二次錯誤,因為記憶體已經釋放

	int & ref2 = test04();
	cout << "ref2=" << ref2 << endl;//輸出10
	cout << "ref2=" << ref2 << endl;//輸出10

	//2.函數的調用可以作為左值
	test04() = 1000;//函數調用作為左值,必須傳回引用,這就相當于a=1000的指派操作

	cout << "函數調用作為左值指派後:" << endl;
	cout << "ref2=" << ref2 << endl;//輸出1000
	cout << "ref2=" << ref2 << endl;//輸出1000



	/*引用的本質*/
	//本質:引用的本質在C++内部實作是一個指針常量,一旦初始化後就不可以發生改變
	int a_bz = 10;          
	//自動轉換為 int* const ref = &a; 指針常量是指針指向不可改,也說明為什麼引用不可更改  
	int& ref_bz = a_bz;  
	ref_bz = 20; //内部發現ref是引用,自動幫我們轉換為: *ref = 20;       
	cout << "a:" << a_bz << endl;  
	cout << "ref:" << ref_bz << endl;      
	func5(a_bz);  



	/*常量引用*/
	//作用:常量引用主要用來修飾形參,防止誤操作
	//在函數形參清單中,可以加const修飾形參,防止形參改變實參
	//文法:傳回值類型 函數名稱(const 變量類型 & 引用名稱){ 函數體語句;return語句;}
	int a_xs = 10;
	//int &ref_xs = 10;//錯誤,引用必須引一塊合法的記憶體空間
	const int & ref_xs = 10;//正确,加上const以後,編譯器将代碼修改為int temp=10;const int & ref_xs=temp;
	//ref_xs = 20;//錯誤,加入const之後變為隻讀,不可以修改

	//列印a_xs
	showValue(a_xs);//輸出1000
	cout << "a_xs=" << a_xs << endl;//輸出1000

	//防止誤操作
	int b_xs = 20;
	showValue2(b_xs);//輸出20
	cout << "b_xs=" << b_xs << endl;//輸出20



	/*函數預設參數*/
	//在C++中,函數的形參清單中的形參是可以有預設值的
	//文法:傳回值類型 函數名稱(參數=預設值){ 函數體語句;return語句;}
	//如果我們自己傳入資料,就用自己的資料;如果沒有,那麼用預設值
	cout << func6(10) << endl;//10+20+30=60
	cout << func6(10,30) << endl;//10+30+30=70
	//注意事項
	//1.如果某個位置已經有了預設參數,那麼從這個位置往後都必須有預設值
	//2.如果函數聲明有預設參數,函數實作就不能有預設參數
	cout << func8(10, 20) << endl;



	/*占位參數*/
	//C++中函數的形參清單裡可以有占位參數,用來做占位,調用函數時必須填補該位置
	//文法:傳回值類型 函數名(參數1,...,資料類型){ 函數體語句;return語句;}
	
	func9(10,1);//占位參數必須填補
	//func9(10) //占位參數有預設值



	/*函數重載*/
	//函數重載滿足條件:
	//1.同一個作用域下
	//2.函數名稱相同
	//3.函數參數類型不同或者個數不同或者順序不同
	//注意:函數的傳回值不可以作為函數重載的條件
	funct();
	funct(10);
	funct(3.14);
	funct(10, 3.14);
	funct(3.14, 10);



	/*函數重載注意事項*/
	//1.引用作為重載條件
	//int a_cz = 10;
	//fun(a_cz); //調用fun(int &a)

	fun(10);//調用fun(const int &a)

	//2.函數重載碰到預設參數
	//fun2(10);//當函數重載碰到預設參數,出現二義性,報錯,盡量避免這種情況



	system("pause");
	return 0;
}
           
c++

繼續閱讀