檔案操作
-
- 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;
}