2019-09-23

f?# C++核心編程

本階段主要針對C++==面向?qū)ο?=編程技術(shù)做詳細(xì)講解讥邻,探討C++中的核心和精髓。

## 1 內(nèi)存分區(qū)模型

C++程序在執(zhí)行時译红,將內(nèi)存大方向劃分為**4個區(qū)域**

- 代碼區(qū):存放函數(shù)體的二進(jìn)制代碼,由操作系統(tǒng)進(jìn)行管理的

- 全局區(qū):存放全局變量和靜態(tài)變量以及常量

- 棧區(qū):由編譯器自動分配釋放, 存放函數(shù)的參數(shù)值,局部變量等

- 堆區(qū):由程序員分配和釋放,若程序員不釋放,程序結(jié)束時由操作系統(tǒng)回收

**內(nèi)存四區(qū)意義:**

不同區(qū)域存放的數(shù)據(jù)择懂,賦予不同的生命周期, 給我們更大的靈活編程

### 1.1 程序運(yùn)行前

? 在程序編譯后,生成了exe可執(zhí)行程序邑遏,**未執(zhí)行該程序前**分為兩個區(qū)域

? **代碼區(qū):**

? 存放 CPU 執(zhí)行的機(jī)器指令

? 代碼區(qū)是**共享**的佣赖,共享的目的是對于頻繁被執(zhí)行的程序,只需要在內(nèi)存中有一份代碼即可

? 代碼區(qū)是**只讀**的无宿,使其只讀的原因是防止程序意外地修改了它的指令

? **全局區(qū):**

? 全局變量和靜態(tài)變量存放在此.

? 全局區(qū)還包含了常量區(qū), 字符串常量和其他常量也存放在此.

? ==該區(qū)域的數(shù)據(jù)在程序結(jié)束后由操作系統(tǒng)釋放==.

**示例:**

```c++

//全局變量

int g_a = 10;

int g_b = 10;

//全局常量

const int c_g_a = 10;

const int c_g_b = 10;

int main() {

//局部變量

int a = 10;

int b = 10;

//打印地址

cout << "局部變量a地址為: " << (int)&a << endl;

cout << "局部變量b地址為: " << (int)&b << endl;

cout << "全局變量g_a地址為: " <<? (int)&g_a << endl;

cout << "全局變量g_b地址為: " <<? (int)&g_b << endl;

//靜態(tài)變量

static int s_a = 10;

static int s_b = 10;

cout << "靜態(tài)變量s_a地址為: " << (int)&s_a << endl;

cout << "靜態(tài)變量s_b地址為: " << (int)&s_b << endl;

cout << "字符串常量地址為: " << (int)&"hello world" << endl;

cout << "字符串常量地址為: " << (int)&"hello world1" << endl;

cout << "全局常量c_g_a地址為: " << (int)&c_g_a << endl;

cout << "全局常量c_g_b地址為: " << (int)&c_g_b << endl;

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;

system("pause");

return 0;

}

```

打印結(jié)果:

![1545017602518](assets/1545017602518.png)

總結(jié):

* C++中在程序運(yùn)行前分為全局區(qū)和代碼區(qū)

* 代碼區(qū)特點(diǎn)是共享和只讀

* 全局區(qū)中存放全局變量茵汰、靜態(tài)變量、常量

* 常量區(qū)中存放 const修飾的全局常量? 和 字符串常量

### 1.2 程序運(yùn)行后

? **棧區(qū):**

? 由編譯器自動分配釋放, 存放函數(shù)的參數(shù)值,局部變量等

? 注意事項(xiàng):不要返回局部變量的地址孽鸡,棧區(qū)開辟的數(shù)據(jù)由編譯器自動釋放

**示例:**

```c++

int * func()

{

int a = 10;

return &a;

}

int main() {

int *p = func();

cout << *p << endl;

cout << *p << endl;

system("pause");

return 0;

}

```

? **堆區(qū):**

? 由程序員分配釋放,若程序員不釋放,程序結(jié)束時由操作系統(tǒng)回收

? 在C++中主要利用new在堆區(qū)開辟內(nèi)存

**示例:**

```c++

int* func()

{

int* a = new int(10);

return a;

}

int main() {

int *p = func();

cout << *p << endl;

cout << *p << endl;


system("pause");

return 0;

}

```

**總結(jié):**

堆區(qū)數(shù)據(jù)由程序員管理開辟和釋放

堆區(qū)數(shù)據(jù)利用new關(guān)鍵字進(jìn)行開辟內(nèi)存

### 1.3 new操作符

? C++中利用==new==操作符在堆區(qū)開辟數(shù)據(jù)

? 堆區(qū)開辟的數(shù)據(jù)蹂午,由程序員手動開辟,手動釋放彬碱,釋放利用操作符 ==delete==

? 語法:` new 數(shù)據(jù)類型`

? 利用new創(chuàng)建的數(shù)據(jù)豆胸,會返回該數(shù)據(jù)對應(yīng)的類型的指針

**示例1: 基本語法**

```c++

int* func()

{

int* a = new int(10);

return a;

}

int main() {

int *p = func();

cout << *p << endl;

cout << *p << endl;

//利用delete釋放堆區(qū)數(shù)據(jù)

delete p;

//cout << *p << endl; //報錯,釋放的空間不可訪問

system("pause");

return 0;

}

```

**示例2:開辟數(shù)組**

```c++

//堆區(qū)開辟數(shù)組

int main() {

int* arr = new int[10];

for (int i = 0; i < 10; i++)

{

arr[i] = i + 100;

}

for (int i = 0; i < 10; i++)

{

cout << arr[i] << endl;

}

//釋放數(shù)組 delete 后加 []

delete[] arr;

system("pause");

return 0;

}

```

## 2 引用

### 2.1 引用的基本使用

**作用: **給變量起別名

**語法:** `數(shù)據(jù)類型 &別名 = 原名`

**示例:**

```C++

int main() {

int a = 10;

int &b = a;

cout << "a = " << a << endl;

cout << "b = " << b << endl;

b = 100;

cout << "a = " << a << endl;

cout << "b = " << b << endl;

system("pause");

return 0;

}

```

### 2.2 引用注意事項(xiàng)

* 引用必須初始化

* 引用在初始化后巷疼,不可以改變

示例:

```C++

int main() {

int a = 10;

int b = 20;

//int &c; //錯誤晚胡,引用必須初始化

int &c = a; //一旦初始化后,就不可以更改

c = b; //這是賦值操作嚼沿,不是更改引用

cout << "a = " << a << endl;

cout << "b = " << b << endl;

cout << "c = " << c << endl;

system("pause");

return 0;

}

```

### 2.3 引用做函數(shù)參數(shù)

**作用:**函數(shù)傳參時估盘,可以利用引用的技術(shù)讓形參修飾實(shí)參

**優(yōu)點(diǎn):**可以簡化指針修改實(shí)參

**示例:**

```C++

//1. 值傳遞

void mySwap01(int a, int b) {

int temp = a;

a = b;

b = temp;

}

//2. 地址傳遞

void mySwap02(int* a, int* b) {

int temp = *a;

*a = *b;

*b = temp;

}

//3. 引用傳遞

void mySwap03(int& a, int& b) {

int temp = a;

a = b;

b = temp;

}

int main() {

int a = 10;

int b = 20;

mySwap01(a, b);

cout << "a:" << a << " b:" << b << endl;

mySwap02(&a, &b);

cout << "a:" << a << " b:" << b << endl;

mySwap03(a, b);

cout << "a:" << a << " b:" << b << endl;

system("pause");

return 0;

}

```

> 總結(jié):通過引用參數(shù)產(chǎn)生的效果同按地址傳遞是一樣的。引用的語法更清楚簡單

### 2.4 引用做函數(shù)返回值

作用:引用是可以作為函數(shù)的返回值存在的

注意:**不要返回局部變量引用**

用法:函數(shù)調(diào)用作為左值

**示例:**

```C++

//返回局部變量引用

int& test01() {

int a = 10; //局部變量

return a;

}

//返回靜態(tài)變量引用

int& test02() {

static int a = 20;

return a;

}

int main() {

//不能返回局部變量的引用

int& ref = test01();

cout << "ref = " << ref << endl;

cout << "ref = " << ref << endl;

//如果函數(shù)做左值骡尽,那么必須返回引用

int& ref2 = test02();

cout << "ref2 = " << ref2 << endl;

cout << "ref2 = " << ref2 << endl;

test02() = 1000;

cout << "ref2 = " << ref2 << endl;

cout << "ref2 = " << ref2 << endl;

system("pause");

return 0;

}

```

?

### 2.5 引用的本質(zhì)

本質(zhì):**引用的本質(zhì)在c++內(nèi)部實(shí)現(xiàn)是一個指針常量.**

講解示例:

```C++

//發(fā)現(xiàn)是引用遣妥,轉(zhuǎn)換為 int* const ref = &a;

void func(int& ref){

ref = 100; // ref是引用,轉(zhuǎn)換為*ref = 100

}

int main(){

int a = 10;


? ? //自動轉(zhuǎn)換為 int* const ref = &a; 指針常量是指針指向不可改攀细,也說明為什么引用不可更改

int& ref = a;

ref = 20; //內(nèi)部發(fā)現(xiàn)ref是引用箫踩,自動幫我們轉(zhuǎn)換為: *ref = 20;


cout << "a:" << a << endl;

cout << "ref:" << ref << endl;


func(a);

return 0;

}

```

結(jié)論:C++推薦用引用技術(shù),因?yàn)檎Z法方便谭贪,引用本質(zhì)是指針常量境钟,但是所有的指針操作編譯器都幫我們做了

### 2.6 常量引用

**作用:**常量引用主要用來修飾形參,防止誤操作

在函數(shù)形參列表中俭识,可以加==const修飾形參==慨削,防止形參改變實(shí)參

**示例:**

```C++

//引用使用的場景,通常用來修飾形參

void showValue(const int& v) {

//v += 10;

cout << v << endl;

}

int main() {

//int& ref = 10;? 引用本身需要一個合法的內(nèi)存空間套媚,因此這行錯誤

//加入const就可以了缚态,編譯器優(yōu)化代碼,int temp = 10; const int& ref = temp;

const int& ref = 10;

//ref = 100;? //加入const后不可以修改變量

cout << ref << endl;

//函數(shù)中利用常量引用防止誤操作修改實(shí)參

int a = 10;

showValue(a);

system("pause");

return 0;

}

```

## 3 函數(shù)提高

### 3.1 函數(shù)默認(rèn)參數(shù)

在C++中凑阶,函數(shù)的形參列表中的形參是可以有默認(rèn)值的猿规。

語法:` 返回值類型? 函數(shù)名 (參數(shù)= 默認(rèn)值){}`

**示例:**

```C++

int func(int a, int b = 10, int c = 10) {

return a + b + c;

}

//1. 如果某個位置參數(shù)有默認(rèn)值,那么從這個位置往后宙橱,從左向右姨俩,必須都要有默認(rèn)值

//2. 如果函數(shù)聲明有默認(rèn)值蘸拔,函數(shù)實(shí)現(xiàn)的時候就不能有默認(rèn)參數(shù)

int func2(int a = 10, int b = 10);

int func2(int a, int b) {

return a + b;

}

int main() {

cout << "ret = " << func(20, 20) << endl;

cout << "ret = " << func(100) << endl;

system("pause");

return 0;

}

```

### 3.2 函數(shù)占位參數(shù)

C++中函數(shù)的形參列表里可以有占位參數(shù),用來做占位环葵,調(diào)用函數(shù)時必須填補(bǔ)該位置

**語法:** `返回值類型 函數(shù)名 (數(shù)據(jù)類型){}`

在現(xiàn)階段函數(shù)的占位參數(shù)存在意義不大调窍,但是后面的課程中會用到該技術(shù)

**示例:**

```C++

//函數(shù)占位參數(shù) ,占位參數(shù)也可以有默認(rèn)參數(shù)

void func(int a, int) {

cout << "this is func" << endl;

}

int main() {

func(10,10); //占位參數(shù)必須填補(bǔ)

system("pause");

return 0;

}

```

### 3.3 函數(shù)重載

#### 3.3.1 函數(shù)重載概述

**作用:**函數(shù)名可以相同张遭,提高復(fù)用性

**函數(shù)重載滿足條件:**

* 同一個作用域下

* 函數(shù)名稱相同

* 函數(shù)參數(shù)**類型不同**? 或者 **個數(shù)不同** 或者 **順序不同**

**注意:**? 函數(shù)的返回值不可以作為函數(shù)重載的條件

**示例:**

```C++

//函數(shù)重載需要函數(shù)都在同一個作用域下

void func()

{

cout << "func 的調(diào)用邓萨!" << endl;

}

void func(int a)

{

cout << "func (int a) 的調(diào)用!" << endl;

}

void func(double a)

{

cout << "func (double a)的調(diào)用菊卷!" << endl;

}

void func(int a ,double b)

{

cout << "func (int a ,double b) 的調(diào)用缔恳!" << endl;

}

void func(double a ,int b)

{

cout << "func (double a ,int b)的調(diào)用!" << endl;

}

//函數(shù)返回值不可以作為函數(shù)重載條件

//int func(double a, int b)

//{

// cout << "func (double a ,int b)的調(diào)用洁闰!" << endl;

//}

int main() {

func();

func(10);

func(3.14);

func(10,3.14);

func(3.14 , 10);

system("pause");

return 0;

}

```

#### 3.3.2 函數(shù)重載注意事項(xiàng)

* 引用作為重載條件

* 函數(shù)重載碰到函數(shù)默認(rèn)參數(shù)

**示例:**

```C++

//函數(shù)重載注意事項(xiàng)

//1歉甚、引用作為重載條件

void func(int &a)

{

cout << "func (int &a) 調(diào)用 " << endl;

}

void func(const int &a)

{

cout << "func (const int &a) 調(diào)用 " << endl;

}

//2、函數(shù)重載碰到函數(shù)默認(rèn)參數(shù)

void func2(int a, int b = 10)

{

cout << "func2(int a, int b = 10) 調(diào)用" << endl;

}

void func2(int a)

{

cout << "func2(int a) 調(diào)用" << endl;

}

int main() {

int a = 10;

func(a); //調(diào)用無const

func(10);//調(diào)用有const

//func2(10); //碰到默認(rèn)參數(shù)產(chǎn)生歧義扑眉,需要避免

system("pause");

return 0;

}

```

## **4** 類和對象

C++面向?qū)ο蟮娜筇匦詾椋?=封裝纸泄、繼承、多態(tài)==

C++認(rèn)為==萬事萬物都皆為對象==腰素,對象上有其屬性和行為

**例如:**

? 人可以作為對象聘裁,屬性有姓名、年齡弓千、身高衡便、體重...,行為有走计呈、跑砰诵、跳征唬、吃飯捌显、唱歌...

? 車也可以作為對象,屬性有輪胎总寒、方向盤扶歪、車燈...,行為有載人、放音樂摄闸、放空調(diào)...

? 具有相同性質(zhì)的==對象==善镰,我們可以抽象稱為==類==,人屬于人類年枕,車屬于車類

### 4.1 封裝

#### 4.1.1? 封裝的意義

封裝是C++面向?qū)ο笕筇匦灾?/p>

封裝的意義:

* 將屬性和行為作為一個整體炫欺,表現(xiàn)生活中的事物

* 將屬性和行為加以權(quán)限控制

**封裝意義一:**

? 在設(shè)計(jì)類的時候,屬性和行為寫在一起熏兄,表現(xiàn)事物

**語法:** `class 類名{? 訪問權(quán)限: 屬性? / 行為? };`

**示例1:**設(shè)計(jì)一個圓類品洛,求圓的周長

**示例代碼:**

```C++

//圓周率

const double PI = 3.14;

//1树姨、封裝的意義

//將屬性和行為作為一個整體,用來表現(xiàn)生活中的事物

//封裝一個圓類桥状,求圓的周長

//class代表設(shè)計(jì)一個類帽揪,后面跟著的是類名

class Circle

{

public:? //訪問權(quán)限? 公共的權(quán)限

//屬性

int m_r;//半徑

//行為

//獲取到圓的周長

double calculateZC()

{

//2 * pi? * r

//獲取圓的周長

return? 2 * PI * m_r;

}

};

int main() {

//通過圓類,創(chuàng)建圓的對象

// c1就是一個具體的圓

Circle c1;

c1.m_r = 10; //給圓對象的半徑 進(jìn)行賦值操作

//2 * pi * 10 = = 62.8

cout << "圓的周長為: " << c1.calculateZC() << endl;

system("pause");

return 0;

}

```

**示例2:**設(shè)計(jì)一個學(xué)生類辅斟,屬性有姓名和學(xué)號转晰,可以給姓名和學(xué)號賦值,可以顯示學(xué)生的姓名和學(xué)號

**示例2代碼:**

```C++

//學(xué)生類

class Student {

public:

void setName(string name) {

m_name = name;

}

void setID(int id) {

m_id = id;

}

void showStudent() {

cout << "name:" << m_name << " ID:" << m_id << endl;

}

public:

string m_name;

int m_id;

};

int main() {

Student stu;

stu.setName("德瑪西亞");

stu.setID(250);

stu.showStudent();

system("pause");

return 0;

}

```

**封裝意義二:**

類在設(shè)計(jì)時士飒,可以把屬性和行為放在不同的權(quán)限下查邢,加以控制

訪問權(quán)限有三種:

1. public? ? ? ? 公共權(quán)限?

2. protected 保護(hù)權(quán)限

3. private? ? ? 私有權(quán)限

**示例:**

```C++

//三種權(quán)限

//公共權(quán)限? public? ? 類內(nèi)可以訪問? 類外可以訪問

//保護(hù)權(quán)限? protected? 類內(nèi)可以訪問? 類外不可以訪問

//私有權(quán)限? private? ? 類內(nèi)可以訪問? 類外不可以訪問

class Person

{

//姓名? 公共權(quán)限

public:

string m_Name;

//汽車? 保護(hù)權(quán)限

protected:

string m_Car;

//銀行卡密碼? 私有權(quán)限

private:

int m_Password;

public:

void func()

{

m_Name = "張三";

m_Car = "拖拉機(jī)";

m_Password = 123456;

}

};

int main() {

Person p;

p.m_Name = "李四";

//p.m_Car = "奔馳";? //保護(hù)權(quán)限類外訪問不到

//p.m_Password = 123; //私有權(quán)限類外訪問不到

system("pause");

return 0;

}

```

#### 4.1.2 struct和class區(qū)別

在C++中 struct和class唯一的**區(qū)別**就在于 **默認(rèn)的訪問權(quán)限不同**

區(qū)別:

* struct 默認(rèn)權(quán)限為公共

* class? 默認(rèn)權(quán)限為私有

```C++

class C1

{

int? m_A; //默認(rèn)是私有權(quán)限

};

struct C2

{

int m_A;? //默認(rèn)是公共權(quán)限

};

int main() {

C1 c1;

c1.m_A = 10; //錯誤,訪問權(quán)限是私有

C2 c2;

c2.m_A = 10; //正確酵幕,訪問權(quán)限是公共

system("pause");

return 0;

}

```

#### 4.1.3 成員屬性設(shè)置為私有

**優(yōu)點(diǎn)1:**將所有成員屬性設(shè)置為私有侠坎,可以自己控制讀寫權(quán)限

**優(yōu)點(diǎn)2:**對于寫權(quán)限,我們可以檢測數(shù)據(jù)的有效性

**示例:**

```C++

class Person {

public:

//姓名設(shè)置可讀可寫

void setName(string name) {

m_Name = name;

}

string getName()

{

return m_Name;

}

//獲取年齡

int getAge() {

return m_Age;

}

//設(shè)置年齡

void setAge(int age) {

if (age < 0 || age > 150) {

cout << "你個老妖精!" << endl;

return;

}

m_Age = age;

}

//情人設(shè)置為只寫

void setLover(string lover) {

m_Lover = lover;

}

private:

string m_Name; //可讀可寫? 姓名

int m_Age; //只讀? 年齡

string m_Lover; //只寫? 情人

};

int main() {

Person p;

//姓名設(shè)置

p.setName("張三");

cout << "姓名: " << p.getName() << endl;

//年齡設(shè)置

p.setAge(50);

cout << "年齡: " << p.getAge() << endl;

//情人設(shè)置

p.setLover("蒼井");

//cout << "情人: " << p.m_Lover << endl;? //只寫屬性裙盾,不可以讀取

system("pause");

return 0;

}

```

**練習(xí)案例1:設(shè)計(jì)立方體類**

設(shè)計(jì)立方體類(Cube)

求出立方體的面積和體積

分別用全局函數(shù)和成員函數(shù)判斷兩個立方體是否相等实胸。

![1545533548532](assets/1545533548532.png)

**練習(xí)案例2:點(diǎn)和圓的關(guān)系**

設(shè)計(jì)一個圓形類(Circle),和一個點(diǎn)類(Point)番官,計(jì)算點(diǎn)和圓的關(guān)系庐完。

![1545533829184](assets/1545533829184.png)

### 4.2 對象的初始化和清理

*? 生活中我們買的電子產(chǎn)品都基本會有出廠設(shè)置,在某一天我們不用時候也會刪除一些自己信息數(shù)據(jù)保證安全

*? C++中的面向?qū)ο髞碓从谏钆侨郏總€對象也都會有初始設(shè)置以及 對象銷毀前的清理數(shù)據(jù)的設(shè)置门躯。

#### 4.2.1 構(gòu)造函數(shù)和析構(gòu)函數(shù)

對象的**初始化和清理**也是兩個非常重要的安全問題

? 一個對象或者變量沒有初始狀態(tài),對其使用后果是未知

? 同樣的使用完一個對象或變量酷师,沒有及時清理讶凉,也會造成一定的安全問題

c++利用了**構(gòu)造函數(shù)**和**析構(gòu)函數(shù)**解決上述問題,這兩個函數(shù)將會被編譯器自動調(diào)用山孔,完成對象初始化和清理工作懂讯。

對象的初始化和清理工作是編譯器強(qiáng)制要我們做的事情,因此如果**我們不提供構(gòu)造和析構(gòu)台颠,編譯器會提供**

**編譯器提供的構(gòu)造函數(shù)和析構(gòu)函數(shù)是空實(shí)現(xiàn)褐望。**

* 構(gòu)造函數(shù):主要作用在于創(chuàng)建對象時為對象的成員屬性賦值,構(gòu)造函數(shù)由編譯器自動調(diào)用串前,無須手動調(diào)用瘫里。

* 析構(gòu)函數(shù):主要作用在于對象**銷毀前**系統(tǒng)自動調(diào)用落塑,執(zhí)行一些清理工作芹务。

**構(gòu)造函數(shù)語法:**`類名(){}`

1. 構(gòu)造函數(shù)侥钳,沒有返回值也不寫void

2. 函數(shù)名稱與類名相同

3. 構(gòu)造函數(shù)可以有參數(shù)舶赔,因此可以發(fā)生重載

4. 程序在調(diào)用對象時候會自動調(diào)用構(gòu)造炎功,無須手動調(diào)用,而且只會調(diào)用一次

**析構(gòu)函數(shù)語法:** `~類名(){}`

1. 析構(gòu)函數(shù)烧董,沒有返回值也不寫void

2. 函數(shù)名稱與類名相同,在名稱前加上符號? ~

3. 析構(gòu)函數(shù)不可以有參數(shù)畔派,因此不可以發(fā)生重載

4. 程序在對象銷毀前會自動調(diào)用析構(gòu)抖甘,無須手動調(diào)用,而且只會調(diào)用一次

```C++

class Person

{

public:

//構(gòu)造函數(shù)

Person()

{

cout << "Person的構(gòu)造函數(shù)調(diào)用" << endl;

}

//析構(gòu)函數(shù)

~Person()

{

cout << "Person的析構(gòu)函數(shù)調(diào)用" << endl;

}

};

void test01()

{

Person p;

}

int main() {

test01();

system("pause");

return 0;

}

```

#### 4.2.2 構(gòu)造函數(shù)的分類及調(diào)用

兩種分類方式:

? 按參數(shù)分為: 有參構(gòu)造和無參構(gòu)造

? 按類型分為: 普通構(gòu)造和拷貝構(gòu)造

三種調(diào)用方式:

? 括號法

? 顯示法

? 隱式轉(zhuǎn)換法

**示例:**

```C++

//1、構(gòu)造函數(shù)分類

// 按照參數(shù)分類分為 有參和無參構(gòu)造? 無參又稱為默認(rèn)構(gòu)造函數(shù)

// 按照類型分類分為 普通構(gòu)造和拷貝構(gòu)造

class Person {

public:

//無參(默認(rèn))構(gòu)造函數(shù)

Person() {

cout << "無參構(gòu)造函數(shù)!" << endl;

}

//有參構(gòu)造函數(shù)

Person(int a) {

age = a;

cout << "有參構(gòu)造函數(shù)!" << endl;

}

//拷貝構(gòu)造函數(shù)

Person(const Person& p) {

age = p.age;

cout << "拷貝構(gòu)造函數(shù)!" << endl;

}

//析構(gòu)函數(shù)

~Person() {

cout << "析構(gòu)函數(shù)!" << endl;

}

public:

int age;

};

//2闷尿、構(gòu)造函數(shù)的調(diào)用

//調(diào)用無參構(gòu)造函數(shù)

void test01() {

Person p; //調(diào)用無參構(gòu)造函數(shù)

}

//調(diào)用有參的構(gòu)造函數(shù)

void test02() {

//2.1? 括號法塑径,常用

Person p1(10);

//注意1:調(diào)用無參構(gòu)造函數(shù)不能加括號,如果加了編譯器認(rèn)為這是一個函數(shù)聲明

//Person p2();

//2.2 顯式法

Person p2 = Person(10);

Person p3 = Person(p2);

//Person(10)單獨(dú)寫就是匿名對象? 當(dāng)前行結(jié)束之后填具,馬上析構(gòu)

//2.3 隱式轉(zhuǎn)換法

Person p4 = 10; // Person p4 = Person(10);

Person p5 = p4; // Person p5 = Person(p4);

//注意2:不能利用 拷貝構(gòu)造函數(shù) 初始化匿名對象 編譯器認(rèn)為是對象聲明

//Person p5(p4);

}

int main() {

test01();

//test02();

system("pause");

return 0;

}

```

#### 4.2.3 拷貝構(gòu)造函數(shù)調(diào)用時機(jī)

C++中拷貝構(gòu)造函數(shù)調(diào)用時機(jī)通常有三種情況

* 使用一個已經(jīng)創(chuàng)建完畢的對象來初始化一個新對象

* 值傳遞的方式給函數(shù)參數(shù)傳值

* 以值方式返回局部對象

**示例:**

```C++

class Person {

public:

Person() {

cout << "無參構(gòu)造函數(shù)!" << endl;

mAge = 0;

}

Person(int age) {

cout << "有參構(gòu)造函數(shù)!" << endl;

mAge = age;

}

Person(const Person& p) {

cout << "拷貝構(gòu)造函數(shù)!" << endl;

mAge = p.mAge;

}

//析構(gòu)函數(shù)在釋放內(nèi)存之前調(diào)用

~Person() {

cout << "析構(gòu)函數(shù)!" << endl;

}

public:

int mAge;

};

//1. 使用一個已經(jīng)創(chuàng)建完畢的對象來初始化一個新對象

void test01() {

Person man(100); //p對象已經(jīng)創(chuàng)建完畢

Person newman(man); //調(diào)用拷貝構(gòu)造函數(shù)

Person newman2 = man; //拷貝構(gòu)造

//Person newman3;

//newman3 = man; //不是調(diào)用拷貝構(gòu)造函數(shù)统舀,賦值操作

}

//2. 值傳遞的方式給函數(shù)參數(shù)傳值

//相當(dāng)于Person p1 = p;

void doWork(Person p1) {}

void test02() {

Person p; //無參構(gòu)造函數(shù)

doWork(p);

}

//3. 以值方式返回局部對象

Person doWork2()

{

Person p1;

cout << (int *)&p1 << endl;

return p1;

}

void test03()

{

Person p = doWork2();

cout << (int *)&p << endl;

}

int main() {

//test01();

//test02();

test03();

system("pause");

return 0;

}

```

#### 4.2.4 構(gòu)造函數(shù)調(diào)用規(guī)則

默認(rèn)情況下,c++編譯器至少給一個類添加3個函數(shù)

1.默認(rèn)構(gòu)造函數(shù)(無參劳景,函數(shù)體為空)

2.默認(rèn)析構(gòu)函數(shù)(無參誉简,函數(shù)體為空)

3.默認(rèn)拷貝構(gòu)造函數(shù),對屬性進(jìn)行值拷貝

構(gòu)造函數(shù)調(diào)用規(guī)則如下:

* 如果用戶定義有參構(gòu)造函數(shù)盟广,c++不在提供默認(rèn)無參構(gòu)造闷串,但是會提供默認(rèn)拷貝構(gòu)造

* 如果用戶定義拷貝構(gòu)造函數(shù),c++不會再提供其他構(gòu)造函數(shù)

示例:

```C++

class Person {

public:

//無參(默認(rèn))構(gòu)造函數(shù)

Person() {

cout << "無參構(gòu)造函數(shù)!" << endl;

}

//有參構(gòu)造函數(shù)

Person(int a) {

age = a;

cout << "有參構(gòu)造函數(shù)!" << endl;

}

//拷貝構(gòu)造函數(shù)

Person(const Person& p) {

age = p.age;

cout << "拷貝構(gòu)造函數(shù)!" << endl;

}

//析構(gòu)函數(shù)

~Person() {

cout << "析構(gòu)函數(shù)!" << endl;

}

public:

int age;

};

void test01()

{

Person p1(18);

//如果不寫拷貝構(gòu)造筋量,編譯器會自動添加拷貝構(gòu)造烹吵,并且做淺拷貝操作

Person p2(p1);

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

}

void test02()

{

//如果用戶提供有參構(gòu)造,編譯器不會提供默認(rèn)構(gòu)造桨武,會提供拷貝構(gòu)造

Person p1; //此時如果用戶自己沒有提供默認(rèn)構(gòu)造肋拔,會出錯

Person p2(10); //用戶提供的有參

Person p3(p2); //此時如果用戶沒有提供拷貝構(gòu)造,編譯器會提供

//如果用戶提供拷貝構(gòu)造呀酸,編譯器不會提供其他構(gòu)造函數(shù)

Person p4; //此時如果用戶自己沒有提供默認(rèn)構(gòu)造凉蜂,會出錯

Person p5(10); //此時如果用戶自己沒有提供有參,會出錯

Person p6(p5); //用戶自己提供拷貝構(gòu)造

}

int main() {

test01();

system("pause");

return 0;

}

```

#### 4.2.5 深拷貝與淺拷貝

深淺拷貝是面試經(jīng)典問題性誉,也是常見的一個坑

淺拷貝:簡單的賦值拷貝操作

深拷貝:在堆區(qū)重新申請空間窿吩,進(jìn)行拷貝操作

**示例:**

```C++

class Person {

public:

//無參(默認(rèn))構(gòu)造函數(shù)

Person() {

cout << "無參構(gòu)造函數(shù)!" << endl;

}

//有參構(gòu)造函數(shù)

Person(int age ,int height) {

cout << "有參構(gòu)造函數(shù)!" << endl;

m_age = age;

m_height = new int(height);

}

//拷貝構(gòu)造函數(shù)?

Person(const Person& p) {

cout << "拷貝構(gòu)造函數(shù)!" << endl;

//如果不利用深拷貝在堆區(qū)創(chuàng)建新內(nèi)存,會導(dǎo)致淺拷貝帶來的重復(fù)釋放堆區(qū)問題

m_age = p.m_age;

m_height = new int(*p.m_height);

}

//析構(gòu)函數(shù)

~Person() {

cout << "析構(gòu)函數(shù)!" << endl;

if (m_height != NULL)

{

delete m_height;

}

}

public:

int m_age;

int* m_height;

};

void test01()

{

Person p1(18, 180);

Person p2(p1);

cout << "p1的年齡: " << p1.m_age << " 身高: " << *p1.m_height << endl;

cout << "p2的年齡: " << p2.m_age << " 身高: " << *p2.m_height << endl;

}

int main() {

test01();

system("pause");

return 0;

}

```

> 總結(jié):如果屬性有在堆區(qū)開辟的错览,一定要自己提供拷貝構(gòu)造函數(shù)纫雁,防止淺拷貝帶來的問題

#### 4.2.6 初始化列表

**作用:**

C++提供了初始化列表語法,用來初始化屬性

**語法:**`構(gòu)造函數(shù)():屬性1(值1),屬性2(值2)... {}`

**示例:**

```C++

class Person {

public:

////傳統(tǒng)方式初始化

//Person(int a, int b, int c) {

// m_A = a;

// m_B = b;

// m_C = c;

//}

//初始化列表方式初始化

Person(int a, int b, int c) :m_A(a), m_B(b), m_C(c) {}

void PrintPerson() {

cout << "mA:" << m_A << endl;

cout << "mB:" << m_B << endl;

cout << "mC:" << m_C << endl;

}

private:

int m_A;

int m_B;

int m_C;

};

int main() {

Person p(1, 2, 3);

p.PrintPerson();

system("pause");

return 0;

}

```

#### 4.2.7 類對象作為類成員

C++類中的成員可以是另一個類的對象蝗砾,我們稱該成員為 對象成員

例如:

```C++

class A {}

class B

{

? ? A a先较;

}

```

B類中有對象A作為成員携冤,A為對象成員

那么當(dāng)創(chuàng)建B對象時悼粮,A與B的構(gòu)造和析構(gòu)的順序是誰先誰后?

**示例:**

```C++

class Phone

{

public:

Phone(string name)

{

m_PhoneName = name;

cout << "Phone構(gòu)造" << endl;

}

~Phone()

{

cout << "Phone析構(gòu)" << endl;

}

string m_PhoneName;

};

class Person

{

public:

//初始化列表可以告訴編譯器調(diào)用哪一個構(gòu)造函數(shù)

Person(string name, string pName) :m_Name(name), m_Phone(pName)

{

cout << "Person構(gòu)造" << endl;

}

~Person()

{

cout << "Person析構(gòu)" << endl;

}

void playGame()

{

cout << m_Name << " 使用" << m_Phone.m_PhoneName << " 牌手機(jī)! " << endl;

}

string m_Name;

Phone m_Phone;

};

void test01()

{

//當(dāng)類中成員是其他類對象時曾棕,我們稱該成員為 對象成員

//構(gòu)造的順序是 :先調(diào)用對象成員的構(gòu)造扣猫,再調(diào)用本類構(gòu)造

//析構(gòu)順序與構(gòu)造相反

Person p("張三" , "蘋果X");

p.playGame();

}

int main() {

test01();

system("pause");

return 0;

}

```

#### 4.2.8 靜態(tài)成員

靜態(tài)成員就是在成員變量和成員函數(shù)前加上關(guān)鍵字static,稱為靜態(tài)成員

靜態(tài)成員分為:

*? 靜態(tài)成員變量

? *? 所有對象共享同一份數(shù)據(jù)

? *? 在編譯階段分配內(nèi)存

? *? 類內(nèi)聲明翘地,類外初始化

*? 靜態(tài)成員函數(shù)

? *? 所有對象共享同一個函數(shù)

? *? 靜態(tài)成員函數(shù)只能訪問靜態(tài)成員變量

**示例1 :**靜態(tài)成員變量

```C++

class Person

{

public:

static int m_A; //靜態(tài)成員變量

//靜態(tài)成員變量特點(diǎn):

//1 在編譯階段分配內(nèi)存

//2 類內(nèi)聲明申尤,類外初始化

//3 所有對象共享同一份數(shù)據(jù)

private:

static int m_B; //靜態(tài)成員變量也是有訪問權(quán)限的

};

int Person::m_A = 10;

int Person::m_B = 10;

void test01()

{

//靜態(tài)成員變量兩種訪問方式

//1癌幕、通過對象

Person p1;

p1.m_A = 100;

cout << "p1.m_A = " << p1.m_A << endl;

Person p2;

p2.m_A = 200;

cout << "p1.m_A = " << p1.m_A << endl; //共享同一份數(shù)據(jù)

cout << "p2.m_A = " << p2.m_A << endl;

//2、通過類名

cout << "m_A = " << Person::m_A << endl;

//cout << "m_B = " << Person::m_B << endl; //私有權(quán)限訪問不到

}

int main() {

test01();

system("pause");

return 0;

}

```

**示例2:**靜態(tài)成員函數(shù)

```C++

class Person

{

public:

//靜態(tài)成員函數(shù)特點(diǎn):

//1 程序共享一個函數(shù)

//2 靜態(tài)成員函數(shù)只能訪問靜態(tài)成員變量

static void func()

{

cout << "func調(diào)用" << endl;

m_A = 100;

//m_B = 100; //錯誤昧穿,不可以訪問非靜態(tài)成員變量

}

static int m_A; //靜態(tài)成員變量

int m_B; //

private:

//靜態(tài)成員函數(shù)也是有訪問權(quán)限的

static void func2()

{

cout << "func2調(diào)用" << endl;

}

};

int Person::m_A = 10;

void test01()

{

//靜態(tài)成員變量兩種訪問方式

//1勺远、通過對象

Person p1;

p1.func();

//2、通過類名

Person::func();

//Person::func2(); //私有權(quán)限訪問不到

}

int main() {

test01();

system("pause");

return 0;

}

```

### 4.3 C++對象模型和this指針

#### 4.3.1 成員變量和成員函數(shù)分開存儲

在C++中时鸵,類內(nèi)的成員變量和成員函數(shù)分開存儲

只有非靜態(tài)成員變量才屬于類的對象上

```C++

class Person {

public:

Person() {

mA = 0;

}

//非靜態(tài)成員變量占對象空間

int mA;

//靜態(tài)成員變量不占對象空間

static int mB;

//函數(shù)也不占對象空間胶逢,所有函數(shù)共享一個函數(shù)實(shí)例

void func() {

cout << "mA:" << this->mA << endl;

}

//靜態(tài)成員函數(shù)也不占對象空間

static void sfunc() {

}

};

int main() {

cout << sizeof(Person) << endl;

system("pause");

return 0;

}

```

#### 4.3.2 this指針概念

通過4.3.1我們知道在C++中成員變量和成員函數(shù)是分開存儲的

每一個非靜態(tài)成員函數(shù)只會誕生一份函數(shù)實(shí)例,也就是說多個同類型的對象會共用一塊代碼

那么問題是:這一塊代碼是如何區(qū)分那個對象調(diào)用自己的呢饰潜?

c++通過提供特殊的對象指針初坠,this指針,解決上述問題彭雾。**this指針指向被調(diào)用的成員函數(shù)所屬的對象**

this指針是隱含每一個非靜態(tài)成員函數(shù)內(nèi)的一種指針

this指針不需要定義碟刺,直接使用即可

this指針的用途:

*? 當(dāng)形參和成員變量同名時,可用this指針來區(qū)分

*? 在類的非靜態(tài)成員函數(shù)中返回對象本身薯酝,可使用return *this

```C++

class Person

{

public:

Person(int age)

{

//1半沽、當(dāng)形參和成員變量同名時,可用this指針來區(qū)分

this->age = age;

}

Person& PersonAddPerson(Person p)

{

this->age += p.age;

//返回對象本身

return *this;

}

int age;

};

void test01()

{

Person p1(10);

cout << "p1.age = " << p1.age << endl;

Person p2(10);

p2.PersonAddPerson(p1).PersonAddPerson(p1).PersonAddPerson(p1);

cout << "p2.age = " << p2.age << endl;

}

int main() {

test01();

system("pause");

return 0;

}

```

#### 4.3.3 空指針訪問成員函數(shù)

C++中空指針也是可以調(diào)用成員函數(shù)的吴菠,但是也要注意有沒有用到this指針

如果用到this指針抄囚,需要加以判斷保證代碼的健壯性

**示例:**

```C++

//空指針訪問成員函數(shù)

class Person {

public:

void ShowClassName() {

cout << "我是Person類!" << endl;

}

void ShowPerson() {

if (this == NULL) {

return;

}

cout << mAge << endl;

}

public:

int mAge;

};

void test01()

{

Person * p = NULL;

p->ShowClassName(); //空指針,可以調(diào)用成員函數(shù)

p->ShowPerson();? //但是如果成員函數(shù)中用到了this指針橄务,就不可以了

}

int main() {

test01();

system("pause");

return 0;

}

```

#### 4.3.4 const修飾成員函數(shù)

**常函數(shù):**

* 成員函數(shù)后加const后我們稱為這個函數(shù)為**常函數(shù)**

* 常函數(shù)內(nèi)不可以修改成員屬性

* 成員屬性聲明時加關(guān)鍵字mutable后幔托,在常函數(shù)中依然可以修改

**常對象:**

* 聲明對象前加const稱該對象為常對象

* 常對象只能調(diào)用常函數(shù)

**示例:**

```C++

class Person {

public:

Person() {

m_A = 0;

m_B = 0;

}

//this指針的本質(zhì)是一個指針常量,指針的指向不可修改

//如果想讓指針指向的值也不可以修改蜂挪,需要聲明常函數(shù)

void ShowPerson() const {

//const Type* const pointer;

//this = NULL; //不能修改指針的指向 Person* const this;

//this->mA = 100; //但是this指針指向的對象的數(shù)據(jù)是可以修改的

//const修飾成員函數(shù)重挑,表示指針指向的內(nèi)存空間的數(shù)據(jù)不能修改,除了mutable修飾的變量

this->m_B = 100;

}

void MyFunc() const {

//mA = 10000;

}

public:

int m_A;

mutable int m_B; //可修改 可變的

};

//const修飾對象? 常對象

void test01() {

const Person person; //常量對象?

cout << person.m_A << endl;

//person.mA = 100; //常對象不能修改成員變量的值,但是可以訪問

person.m_B = 100; //但是常對象可以修改mutable修飾成員變量

//常對象訪問成員函數(shù)

person.MyFunc(); //常對象不能調(diào)用const的函數(shù)

}

int main() {

test01();

system("pause");

return 0;

}

```

### 4.4 友元

生活中你的家有客廳(Public)棠涮,有你的臥室(Private)

客廳所有來的客人都可以進(jìn)去谬哀,但是你的臥室是私有的,也就是說只有你能進(jìn)去

但是呢严肪,你也可以允許你的好閨蜜好基友進(jìn)去史煎。

在程序里,有些私有屬性 也想讓類外特殊的一些函數(shù)或者類進(jìn)行訪問驳糯,就需要用到友元的技術(shù)

友元的目的就是讓一個函數(shù)或者類 訪問另一個類中私有成員

友元的關(guān)鍵字為? ==friend==

友元的三種實(shí)現(xiàn)

* 全局函數(shù)做友元

* 類做友元

* 成員函數(shù)做友元

#### 4.4.1 全局函數(shù)做友元

```C++

class Building

{

//告訴編譯器 goodGay全局函數(shù) 是 Building類的好朋友篇梭,可以訪問類中的私有內(nèi)容

friend void goodGay(Building * building);

public:

Building()

{

this->m_SittingRoom = "客廳";

this->m_BedRoom = "臥室";

}

public:

string m_SittingRoom; //客廳

private:

string m_BedRoom; //臥室

};

void goodGay(Building * building)

{

cout << "好基友正在訪問: " << building->m_SittingRoom << endl;

cout << "好基友正在訪問: " << building->m_BedRoom << endl;

}

void test01()

{

Building b;

goodGay(&b);

}

int main(){

test01();

system("pause");

return 0;

}

```

#### 4.4.2 類做友元

```C++

class Building;

class goodGay

{

public:

goodGay();

void visit();

private:

Building *building;

};

class Building

{

//告訴編譯器 goodGay類是Building類的好朋友,可以訪問到Building類中私有內(nèi)容

friend class goodGay;

public:

Building();

public:

string m_SittingRoom; //客廳

private:

string m_BedRoom;//臥室

};

Building::Building()

{

this->m_SittingRoom = "客廳";

this->m_BedRoom = "臥室";

}

goodGay::goodGay()

{

building = new Building;

}

void goodGay::visit()

{

cout << "好基友正在訪問" << building->m_SittingRoom << endl;

cout << "好基友正在訪問" << building->m_BedRoom << endl;

}

void test01()

{

goodGay gg;

gg.visit();

}

int main(){

test01();

system("pause");

return 0;

}

```

#### 4.4.3 成員函數(shù)做友元

```C++

class Building;

class goodGay

{

public:

goodGay();

void visit(); //只讓visit函數(shù)作為Building的好朋友酝枢,可以發(fā)訪問Building中私有內(nèi)容

void visit2();

private:

Building *building;

};

class Building

{

//告訴編譯器? goodGay類中的visit成員函數(shù) 是Building好朋友恬偷,可以訪問私有內(nèi)容

friend void goodGay::visit();

public:

Building();

public:

string m_SittingRoom; //客廳

private:

string m_BedRoom;//臥室

};

Building::Building()

{

this->m_SittingRoom = "客廳";

this->m_BedRoom = "臥室";

}

goodGay::goodGay()

{

building = new Building;

}

void goodGay::visit()

{

cout << "好基友正在訪問" << building->m_SittingRoom << endl;

cout << "好基友正在訪問" << building->m_BedRoom << endl;

}

void goodGay::visit2()

{

cout << "好基友正在訪問" << building->m_SittingRoom << endl;

//cout << "好基友正在訪問" << building->m_BedRoom << endl;

}

void test01()

{

goodGay? gg;

gg.visit();

}

int main(){


test01();

system("pause");

return 0;

}

```

### 4.5 運(yùn)算符重載

運(yùn)算符重載概念:對已有的運(yùn)算符重新進(jìn)行定義,賦予其另一種功能帘睦,以適應(yīng)不同的數(shù)據(jù)類型

#### 4.5.1 加號運(yùn)算符重載

作用:實(shí)現(xiàn)兩個自定義數(shù)據(jù)類型相加的運(yùn)算

```C++

class Person {

public:

Person() {};

Person(int a, int b)

{

this->m_A = a;

this->m_B = b;

}

//成員函數(shù)實(shí)現(xiàn) + 號運(yùn)算符重載

Person operator+(const Person& p) {

Person 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;

};

//全局函數(shù)實(shí)現(xiàn) + 號運(yùn)算符重載

//Person operator+(const Person& p1, const Person& p2) {

// Person temp(0, 0);

// temp.m_A = p1.m_A + p2.m_A;

// temp.m_B = p1.m_B + p2.m_B;

// return temp;

//}

//運(yùn)算符重載 可以發(fā)生函數(shù)重載

Person operator+(const Person& p2, int val)?

{

Person temp;

temp.m_A = p2.m_A + val;

temp.m_B = p2.m_B + val;

return temp;

}

void test() {

Person p1(10, 10);

Person p2(20, 20);

//成員函數(shù)方式

Person p3 = p2 + p1;? //相當(dāng)于 p2.operaor+(p1)

cout << "mA:" << p3.m_A << " mB:" << p3.m_B << endl;

Person p4 = p3 + 10; //相當(dāng)于 operator+(p3,10)

cout << "mA:" << p4.m_A << " mB:" << p4.m_B << endl;

}

int main() {

test();

system("pause");

return 0;

}

```

> 總結(jié)1:對于內(nèi)置的數(shù)據(jù)類型的表達(dá)式的的運(yùn)算符是不可能改變的

> 總結(jié)2:不要濫用運(yùn)算符重載

#### 4.5.2 左移運(yùn)算符重載

作用:可以輸出自定義數(shù)據(jù)類型

```C++

class Person {

friend ostream& operator<<(ostream& out, Person& p);

public:

Person(int a, int b)

{

this->m_A = a;

this->m_B = b;

}

//成員函數(shù) 實(shí)現(xiàn)不了? p << cout 不是我們想要的效果

//void operator<<(Person& p){

//}

private:

int m_A;

int m_B;

};

//全局函數(shù)實(shí)現(xiàn)左移重載

//ostream對象只能有一個

ostream& operator<<(ostream& out, Person& p) {

out << "a:" << p.m_A << " b:" << p.m_B;

return out;

}

void test() {

Person p1(10, 20);

cout << p1 << "hello world" << endl; //鏈?zhǔn)骄幊?/p>

}

int main() {

test();

system("pause");

return 0;

}

```

> 總結(jié):重載左移運(yùn)算符配合友元可以實(shí)現(xiàn)輸出自定義數(shù)據(jù)類型

#### 4.5.3 遞增運(yùn)算符重載

作用: 通過重載遞增運(yùn)算符袍患,實(shí)現(xiàn)自己的整型數(shù)據(jù)

```C++

class MyInteger {

friend ostream& operator<<(ostream& out, MyInteger myint);

public:

MyInteger() {

m_Num = 0;

}

//前置++

MyInteger& operator++() {

//先++

m_Num++;

//再返回

return *this;

}

//后置++

MyInteger operator++(int) {

//先返回

MyInteger temp = *this; //記錄當(dāng)前本身的值坦康,然后讓本身的值加1,但是返回的是以前的值诡延,達(dá)到先返回后++滞欠;

m_Num++;

return temp;

}

private:

int m_Num;

};

ostream& operator<<(ostream& out, MyInteger myint) {

out << myint.m_Num;

return out;

}

//前置++ 先++ 再返回

void test01() {

MyInteger myInt;

cout << ++myInt << endl;

cout << myInt << endl;

}

//后置++ 先返回 再++

void test02() {

MyInteger myInt;

cout << myInt++ << endl;

cout << myInt << endl;

}

int main() {

test01();

//test02();

system("pause");

return 0;

}

```

> 總結(jié): 前置遞增返回引用,后置遞增返回值

#### 4.5.4 賦值運(yùn)算符重載

c++編譯器至少給一個類添加4個函數(shù)

1. 默認(rèn)構(gòu)造函數(shù)(無參肆良,函數(shù)體為空)

2. 默認(rèn)析構(gòu)函數(shù)(無參仑撞,函數(shù)體為空)

3. 默認(rèn)拷貝構(gòu)造函數(shù),對屬性進(jìn)行值拷貝

4. 賦值運(yùn)算符 operator=, 對屬性進(jìn)行值拷貝

如果類中有屬性指向堆區(qū)妖滔,做賦值操作時也會出現(xiàn)深淺拷貝問題

**示例:**

```C++

class Person

{

public:

Person(int age)

{

//將年齡數(shù)據(jù)開辟到堆區(qū)

m_Age = new int(age);

}

//重載賦值運(yùn)算符

Person& operator=(Person &p)

{

if (m_Age != NULL)

{

delete m_Age;

m_Age = NULL;

}

//編譯器提供的代碼是淺拷貝

//m_Age = p.m_Age;

//提供深拷貝 解決淺拷貝的問題

m_Age = new int(*p.m_Age);

//返回自身

return *this;

}

~Person()

{

if (m_Age != NULL)

{

delete m_Age;

m_Age = NULL;

}

}

//年齡的指針

int *m_Age;

};

void test01()

{

Person p1(18);

Person p2(20);

Person p3(30);

p3 = p2 = p1; //賦值操作

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

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

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

}

int main() {

test01();

//int a = 10;

//int b = 20;

//int c = 30;

//c = b = a;

//cout << "a = " << a << endl;

//cout << "b = " << b << endl;

//cout << "c = " << c << endl;

system("pause");

return 0;

}

```

#### 4.5.5 關(guān)系運(yùn)算符重載

**作用:**重載關(guān)系運(yùn)算符隧哮,可以讓兩個自定義類型對象進(jìn)行對比操作

**示例:**

```C++

class Person

{

public:

Person(string name, int age)

{

this->m_Name = name;

this->m_Age = age;

};

bool operator==(Person & p)

{

if (this->m_Name == p.m_Name && this->m_Age == p.m_Age)

{

return true;

}

else

{

return false;

}

}

bool operator!=(Person & p)

{

if (this->m_Name == p.m_Name && this->m_Age == p.m_Age)

{

return false;

}

else

{

return true;

}

}

string m_Name;

int m_Age;

};

void test01()

{

//int a = 0;

//int b = 0;

Person a("孫悟空", 18);

Person b("孫悟空", 18);

if (a == b)

{

cout << "a和b相等" << endl;

}

else

{

cout << "a和b不相等" << endl;

}

if (a != b)

{

cout << "a和b不相等" << endl;

}

else

{

cout << "a和b相等" << endl;

}

}

int main() {

test01();

system("pause");

return 0;

}

```

#### 4.5.6 函數(shù)調(diào)用運(yùn)算符重載

* 函數(shù)調(diào)用運(yùn)算符 ()? 也可以重載

* 由于重載后使用的方式非常像函數(shù)的調(diào)用,因此稱為仿函數(shù)

* 仿函數(shù)沒有固定寫法座舍,非常靈活

**示例:**

```C++

class MyPrint

{

public:

void operator()(string text)

{

cout << text << endl;

}

};

void test01()

{

//重載的()操作符 也稱為仿函數(shù)

MyPrint myFunc;

myFunc("hello world");

}

class MyAdd

{

public:

int operator()(int v1, int v2)

{

return v1 + v2;

}

};

void test02()

{

MyAdd add;

int ret = add(10, 10);

cout << "ret = " << ret << endl;

//匿名對象調(diào)用?

cout << "MyAdd()(100,100) = " << MyAdd()(100, 100) << endl;

}

int main() {

test01();

test02();

system("pause");

return 0;

}

```

### 4.6? 繼承

**繼承是面向?qū)ο笕筇匦灾?*

有些類與類之間存在特殊的關(guān)系沮翔,例如下圖中:

![1544861202252](assets/1544861202252.png)

我們發(fā)現(xiàn),定義這些類時曲秉,下級別的成員除了擁有上一級的共性采蚀,還有自己的特性。

這個時候我們就可以考慮利用繼承的技術(shù)承二,減少重復(fù)代碼

#### 4.6.1 繼承的基本語法

例如我們看到很多網(wǎng)站中榆鼠,都有公共的頭部,公共的底部亥鸠,甚至公共的左側(cè)列表妆够,只有中心內(nèi)容不同

接下來我們分別利用普通寫法和繼承的寫法來實(shí)現(xiàn)網(wǎng)頁中的內(nèi)容,看一下繼承存在的意義以及好處

**普通實(shí)現(xiàn):**

```C++

//Java頁面

class Java

{

public:

void header()

{

cout << "首頁负蚊、公開課神妹、登錄、注冊...(公共頭部)" << endl;

}

void footer()

{

cout << "幫助中心家妆、交流合作鸵荠、站內(nèi)地圖...(公共底部)" << endl;

}

void left()

{

cout << "Java,Python,C++...(公共分類列表)" << endl;

}

void content()

{

cout << "JAVA學(xué)科視頻" << endl;

}

};

//Python頁面

class Python

{

public:

void header()

{

cout << "首頁、公開課伤极、登錄蛹找、注冊...(公共頭部)" << endl;

}

void footer()

{

cout << "幫助中心、交流合作哨坪、站內(nèi)地圖...(公共底部)" << endl;

}

void left()

{

cout << "Java,Python,C++...(公共分類列表)" << endl;

}

void content()

{

cout << "Python學(xué)科視頻" << endl;

}

};

//C++頁面

class CPP

{

public:

void header()

{

cout << "首頁庸疾、公開課、登錄齿税、注冊...(公共頭部)" << endl;

}

void footer()

{

cout << "幫助中心彼硫、交流合作、站內(nèi)地圖...(公共底部)" << endl;

}

void left()

{

cout << "Java,Python,C++...(公共分類列表)" << endl;

}

void content()

{

cout << "C++學(xué)科視頻" << endl;

}

};

void test01()

{

//Java頁面

cout << "Java下載視頻頁面如下: " << endl;

Java ja;

ja.header();

ja.footer();

ja.left();

ja.content();

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

//Python頁面

cout << "Python下載視頻頁面如下: " << endl;

Python py;

py.header();

py.footer();

py.left();

py.content();

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

//C++頁面

cout << "C++下載視頻頁面如下: " << endl;

CPP cp;

cp.header();

cp.footer();

cp.left();

cp.content();

}

int main() {

test01();

system("pause");

return 0;

}

```

**繼承實(shí)現(xiàn):**

```C++

//公共頁面

class BasePage

{

public:

void header()

{

cout << "首頁凌箕、公開課拧篮、登錄、注冊...(公共頭部)" << endl;

}

void footer()

{

cout << "幫助中心牵舱、交流合作串绩、站內(nèi)地圖...(公共底部)" << endl;

}

void left()

{

cout << "Java,Python,C++...(公共分類列表)" << endl;

}

};

//Java頁面

class Java : public BasePage

{

public:

void content()

{

cout << "JAVA學(xué)科視頻" << endl;

}

};

//Python頁面

class Python : public BasePage

{

public:

void content()

{

cout << "Python學(xué)科視頻" << endl;

}

};

//C++頁面

class CPP : public BasePage

{

public:

void content()

{

cout << "C++學(xué)科視頻" << endl;

}

};

void test01()

{

//Java頁面

cout << "Java下載視頻頁面如下: " << endl;

Java ja;

ja.header();

ja.footer();

ja.left();

ja.content();

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

//Python頁面

cout << "Python下載視頻頁面如下: " << endl;

Python py;

py.header();

py.footer();

py.left();

py.content();

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

//C++頁面

cout << "C++下載視頻頁面如下: " << endl;

CPP cp;

cp.header();

cp.footer();

cp.left();

cp.content();

}

int main() {

test01();

system("pause");

return 0;

}

```

**總結(jié):**

繼承的好處:==可以減少重復(fù)的代碼==

class A : public B;

A 類稱為子類 或 派生類

B 類稱為父類 或 基類

**派生類中的成員,包含兩大部分**:

一類是從基類繼承過來的芜壁,一類是自己增加的成員礁凡。

從基類繼承過過來的表現(xiàn)其共性,而新增的成員體現(xiàn)了其個性慧妄。

#### 4.6.2 繼承方式

繼承的語法:`class 子類 : 繼承方式? 父類`

**繼承方式一共有三種:**

* 公共繼承

* 保護(hù)繼承

* 私有繼承

![img](assets/clip_image002.png)

**示例:**

```C++

class Base1

{

public:

int m_A;

protected:

int m_B;

private:

int m_C;

};

//公共繼承

class Son1 :public Base1

{

public:

void func()

{

m_A; //可訪問 public權(quán)限

m_B; //可訪問 protected權(quán)限

//m_C; //不可訪問

}

};

void myClass()

{

Son1 s1;

s1.m_A; //其他類只能訪問到公共權(quán)限

}

//保護(hù)繼承

class Base2

{

public:

int m_A;

protected:

int m_B;

private:

int m_C;

};

class Son2:protected Base2

{

public:

void func()

{

m_A; //可訪問 protected權(quán)限

m_B; //可訪問 protected權(quán)限

//m_C; //不可訪問

}

};

void myClass2()

{

Son2 s;

//s.m_A; //不可訪問

}

//私有繼承

class Base3

{

public:

int m_A;

protected:

int m_B;

private:

int m_C;

};

class Son3:private Base3

{

public:

void func()

{

m_A; //可訪問 private權(quán)限

m_B; //可訪問 private權(quán)限

//m_C; //不可訪問

}

};

class GrandSon3 :public Son3

{

public:

void func()

{

//Son3是私有繼承顷牌,所以繼承Son3的屬性在GrandSon3中都無法訪問到

//m_A;

//m_B;

//m_C;

}

};

```

#### 4.6.3 繼承中的對象模型

**問題:**從父類繼承過來的成員,哪些屬于子類對象中塞淹?

**示例:**

```C++

class Base

{

public:

int m_A;

protected:

int m_B;

private:

int m_C; //私有成員只是被隱藏了窟蓝,但是還是會繼承下去

};

//公共繼承

class Son :public Base

{

public:

int m_D;

};

void test01()

{

cout << "sizeof Son = " << sizeof(Son) << endl;

}

int main() {

test01();

system("pause");

return 0;

}

```

利用工具查看:

![1545881904150](assets/1545881904150.png)

打開工具窗口后,定位到當(dāng)前CPP文件的盤符

然后輸入: cl /d1 reportSingleClassLayout查看的類名? 所屬文件名

效果如下圖:

![1545882158050](assets/1545882158050.png)

> 結(jié)論: 父類中私有成員也是被子類繼承下去了饱普,只是由編譯器給隱藏后訪問不到

#### 4.6.4 繼承中構(gòu)造和析構(gòu)順序

子類繼承父類后运挫,當(dāng)創(chuàng)建子類對象,也會調(diào)用父類的構(gòu)造函數(shù)

問題:父類和子類的構(gòu)造和析構(gòu)順序是誰先誰后套耕?

**示例:**

```C++

class Base

{

public:

Base()

{

cout << "Base構(gòu)造函數(shù)!" << endl;

}

~Base()

{

cout << "Base析構(gòu)函數(shù)!" << endl;

}

};

class Son : public Base

{

public:

Son()

{

cout << "Son構(gòu)造函數(shù)!" << endl;

}

~Son()

{

cout << "Son析構(gòu)函數(shù)!" << endl;

}

};

void test01()

{

//繼承中 先調(diào)用父類構(gòu)造函數(shù)谁帕,再調(diào)用子類構(gòu)造函數(shù),析構(gòu)順序與構(gòu)造相反

Son s;

}

int main() {

test01();

system("pause");

return 0;

}

```

> 總結(jié):繼承中 先調(diào)用父類構(gòu)造函數(shù)冯袍,再調(diào)用子類構(gòu)造函數(shù)匈挖,析構(gòu)順序與構(gòu)造相反

#### 4.6.5 繼承同名成員處理方式

問題:當(dāng)子類與父類出現(xiàn)同名的成員,如何通過子類對象康愤,訪問到子類或父類中同名的數(shù)據(jù)呢关划?

* 訪問子類同名成員? 直接訪問即可

* 訪問父類同名成員? 需要加作用域

**示例:**

```C++

class Base {

public:

Base()

{

m_A = 100;

}

void func()

{

cout << "Base - func()調(diào)用" << endl;

}

void func(int a)

{

cout << "Base - func(int a)調(diào)用" << endl;

}

public:

int m_A;

};

class Son : public Base {

public:

Son()

{

m_A = 200;

}

//當(dāng)子類與父類擁有同名的成員函數(shù),子類會隱藏父類中所有版本的同名成員函數(shù)

//如果想訪問父類中被隱藏的同名成員函數(shù)翘瓮,需要加父類的作用域

void func()

{

cout << "Son - func()調(diào)用" << endl;

}

public:

int m_A;

};

void test01()

{

Son s;

cout << "Son下的m_A = " << s.m_A << endl;

cout << "Base下的m_A = " << s.Base::m_A << endl;

s.func();

s.Base::func();

s.Base::func(10);

}

int main() {

test01();

system("pause");

return EXIT_SUCCESS;

}

```

總結(jié):

1. 子類對象可以直接訪問到子類中同名成員

2. 子類對象加作用域可以訪問到父類同名成員

3. 當(dāng)子類與父類擁有同名的成員函數(shù)贮折,子類會隱藏父類中同名成員函數(shù),加作用域可以訪問到父類中同名函數(shù)

#### 4.6.6 繼承同名靜態(tài)成員處理方式

問題:繼承中同名的靜態(tài)成員在子類對象上如何進(jìn)行訪問资盅?

靜態(tài)成員和非靜態(tài)成員出現(xiàn)同名调榄,處理方式一致

- 訪問子類同名成員? 直接訪問即可

- 訪問父類同名成員? 需要加作用域

**示例:**

```C++

class Base {

public:

static void func()

{

cout << "Base - static void func()" << endl;

}

static void func(int a)

{

cout << "Base - static void func(int a)" << endl;

}

static int m_A;

};

int Base::m_A = 100;

class Son : public Base {

public:

static void func()

{

cout << "Son - static void func()" << endl;

}

static int m_A;

};

int Son::m_A = 200;

//同名成員屬性

void test01()

{

//通過對象訪問

cout << "通過對象訪問: " << endl;

Son s;

cout << "Son? 下 m_A = " << s.m_A << endl;

cout << "Base 下 m_A = " << s.Base::m_A << endl;

//通過類名訪問

cout << "通過類名訪問: " << endl;

cout << "Son? 下 m_A = " << Son::m_A << endl;

cout << "Base 下 m_A = " << Son::Base::m_A << endl;

}

//同名成員函數(shù)

void test02()

{

//通過對象訪問

cout << "通過對象訪問: " << endl;

Son s;

s.func();

s.Base::func();

cout << "通過類名訪問: " << endl;

Son::func();

Son::Base::func();

//出現(xiàn)同名,子類會隱藏掉父類中所有同名成員函數(shù)呵扛,需要加作作用域訪問

Son::Base::func(100);

}

int main() {

//test01();

test02();

system("pause");

return 0;

}

```

> 總結(jié):同名靜態(tài)成員處理方式和非靜態(tài)處理方式一樣每庆,只不過有兩種訪問的方式(通過對象 和 通過類名)

#### 4.6.7 多繼承語法

C++允許**一個類繼承多個類**

語法:` class 子類 :繼承方式 父類1 , 繼承方式 父類2...`

多繼承可能會引發(fā)父類中有同名成員出現(xiàn)今穿,需要加作用域區(qū)分

**C++實(shí)際開發(fā)中不建議用多繼承**

**示例:**

```C++

class Base1 {

public:

Base1()

{

m_A = 100;

}

public:

int m_A;

};

class Base2 {

public:

Base2()

{

m_A = 200;? //開始是m_B 不會出問題缤灵,但是改為mA就會出現(xiàn)不明確

}

public:

int m_A;

};

//語法:class 子類:繼承方式 父類1 ,繼承方式 父類2

class Son : public Base2, public Base1

{

public:

Son()

{

m_C = 300;

m_D = 400;

}

public:

int m_C;

int m_D;

};

//多繼承容易產(chǎn)生成員同名的情況

//通過使用類名作用域可以區(qū)分調(diào)用哪一個基類的成員

void test01()

{

Son s;

cout << "sizeof Son = " << sizeof(s) << endl;

cout << s.Base1::m_A << endl;

cout << s.Base2::m_A << endl;

}

int main() {

test01();

system("pause");

return 0;

}

```

> 總結(jié): 多繼承中如果父類中出現(xiàn)了同名情況,子類使用時候要加作用域

#### 4.6.8 菱形繼承

**菱形繼承概念:**

? 兩個派生類繼承同一個基類

? 又有某個類同時繼承者兩個派生類

? 這種繼承被稱為菱形繼承腮出,或者鉆石繼承

**典型的菱形繼承案例:**

![IMG_256](assets/clip_image002.jpg)

**菱形繼承問題:**

1.? ? 羊繼承了動物的數(shù)據(jù)帖鸦,駝同樣繼承了動物的數(shù)據(jù),當(dāng)草泥馬使用數(shù)據(jù)時胚嘲,就會產(chǎn)生二義性作儿。

2. 草泥馬繼承自動物的數(shù)據(jù)繼承了兩份,其實(shí)我們應(yīng)該清楚馋劈,這份數(shù)據(jù)我們只需要一份就可以攻锰。

**示例:**

```C++

class Animal

{

public:

int m_Age;

};

//繼承前加virtual關(guān)鍵字后,變?yōu)樘摾^承

//此時公共的父類Animal稱為虛基類

class Sheep : virtual public Animal {};

class Tuo? : virtual public Animal {};

class SheepTuo : public Sheep, public Tuo {};

void test01()

{

SheepTuo st;

st.Sheep::m_Age = 100;

st.Tuo::m_Age = 200;

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;

}

int main() {

test01();

system("pause");

return 0;

}

```

總結(jié):

* 菱形繼承帶來的主要問題是子類繼承兩份相同的數(shù)據(jù)妓雾,導(dǎo)致資源浪費(fèi)以及毫無意義

* 利用虛繼承可以解決菱形繼承問題

### 4.7? 多態(tài)

#### 4.7.1 多態(tài)的基本概念

**多態(tài)是C++面向?qū)ο笕筇匦灾?*

多態(tài)分為兩類

* 靜態(tài)多態(tài): 函數(shù)重載 和 運(yùn)算符重載屬于靜態(tài)多態(tài)娶吞,復(fù)用函數(shù)名

* 動態(tài)多態(tài): 派生類和虛函數(shù)實(shí)現(xiàn)運(yùn)行時多態(tài)

靜態(tài)多態(tài)和動態(tài)多態(tài)區(qū)別:

* 靜態(tài)多態(tài)的函數(shù)地址早綁定? -? 編譯階段確定函數(shù)地址

* 動態(tài)多態(tài)的函數(shù)地址晚綁定? -? 運(yùn)行階段確定函數(shù)地址

下面通過案例進(jìn)行講解多態(tài)

```C++

class Animal

{

public:

//Speak函數(shù)就是虛函數(shù)

//函數(shù)前面加上virtual關(guān)鍵字,變成虛函數(shù)械姻,那么編譯器在編譯的時候就不能確定函數(shù)調(diào)用了妒蛇。

virtual void speak()

{

cout << "動物在說話" << endl;

}

};

class Cat :public Animal

{

public:

void speak()

{

cout << "小貓?jiān)谡f話" << endl;

}

};

class Dog :public Animal

{

public:

void speak()

{

cout << "小狗在說話" << endl;

}

};

//我們希望傳入什么對象,那么就調(diào)用什么對象的函數(shù)

//如果函數(shù)地址在編譯階段就能確定策添,那么靜態(tài)聯(lián)編

//如果函數(shù)地址在運(yùn)行階段才能確定材部,就是動態(tài)聯(lián)編

void DoSpeak(Animal & animal)

{

animal.speak();

}

//

//多態(tài)滿足條件:

//1、有繼承關(guān)系

//2唯竹、子類重寫父類中的虛函數(shù)

//多態(tài)使用:

//父類指針或引用指向子類對象

void test01()

{

Cat cat;

DoSpeak(cat);

Dog dog;

DoSpeak(dog);

}

int main() {

test01();

system("pause");

return 0;

}

```

總結(jié):

多態(tài)滿足條件

* 有繼承關(guān)系

* 子類重寫父類中的虛函數(shù)

多態(tài)使用條件

* 父類指針或引用指向子類對象

重寫:函數(shù)返回值類型? 函數(shù)名 參數(shù)列表 完全一致稱為重寫

#### 4.7.2 多態(tài)案例一-計(jì)算器類

案例描述:

分別利用普通寫法和多態(tài)技術(shù)乐导,設(shè)計(jì)實(shí)現(xiàn)兩個操作數(shù)進(jìn)行運(yùn)算的計(jì)算器類

多態(tài)的優(yōu)點(diǎn):

* 代碼組織結(jié)構(gòu)清晰

* 可讀性強(qiáng)

* 利于前期和后期的擴(kuò)展以及維護(hù)

**示例:**

```C++

//普通實(shí)現(xiàn)

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;

}

//如果要提供新的運(yùn)算,需要修改源碼

}

public:

int m_Num1;

int m_Num2;

};

void test01()

{

//普通實(shí)現(xiàn)測試

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;

}

//多態(tài)實(shí)現(xiàn)

//抽象計(jì)算器類

//多態(tài)優(yōu)點(diǎn):代碼組織結(jié)構(gòu)清晰浸颓,可讀性強(qiáng)物臂,利于前期和后期的擴(kuò)展以及維護(hù)

class AbstractCalculator

{

public :

virtual int getResult()

{

return 0;

}

int m_Num1;

int m_Num2;

};

//加法計(jì)算器

class AddCalculator :public AbstractCalculator

{

public:

int getResult()

{

return m_Num1 + m_Num2;

}

};

//減法計(jì)算器

class SubCalculator :public AbstractCalculator

{

public:

int getResult()

{

return m_Num1 - m_Num2;

}

};

//乘法計(jì)算器

class MulCalculator :public AbstractCalculator

{

public:

int getResult()

{

return m_Num1 * m_Num2;

}

};

void test02()

{

//創(chuàng)建加法計(jì)算器

AbstractCalculator *abc = new AddCalculator;

abc->m_Num1 = 10;

abc->m_Num2 = 10;

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

delete abc;? //用完了記得銷毀

//創(chuàng)建減法計(jì)算器

abc = new SubCalculator;

abc->m_Num1 = 10;

abc->m_Num2 = 10;

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

delete abc;?

//創(chuàng)建乘法計(jì)算器

abc = new MulCalculator;

abc->m_Num1 = 10;

abc->m_Num2 = 10;

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

delete abc;

}

int main() {

//test01();

test02();

system("pause");

return 0;

}

```

> 總結(jié):C++開發(fā)提倡利用多態(tài)設(shè)計(jì)程序架構(gòu),因?yàn)槎鄳B(tài)優(yōu)點(diǎn)很多

#### 4.7.3 純虛函數(shù)和抽象類

在多態(tài)中产上,通常父類中虛函數(shù)的實(shí)現(xiàn)是毫無意義的棵磷,主要都是調(diào)用子類重寫的內(nèi)容

因此可以將虛函數(shù)改為**純虛函數(shù)**

純虛函數(shù)語法:`virtual 返回值類型 函數(shù)名 (參數(shù)列表)= 0 ;`

當(dāng)類中有了純虛函數(shù),這個類也稱為==抽象類==

**抽象類特點(diǎn)**:

* 無法實(shí)例化對象

* 子類必須重寫抽象類中的純虛函數(shù)晋涣,否則也屬于抽象類

**示例:**

```C++

class Base

{

public:

//純虛函數(shù)

//類中只要有一個純虛函數(shù)就稱為抽象類

//抽象類無法實(shí)例化對象

//子類必須重寫父類中的純虛函數(shù)仪媒,否則也屬于抽象類

virtual void func() = 0;

};

class Son :public Base

{

public:

virtual void func()

{

cout << "func調(diào)用" << endl;

};

};

void test01()

{

Base * base = NULL;

//base = new Base; // 錯誤,抽象類無法實(shí)例化對象

base = new Son;

base->func();

delete base;//記得銷毀

}

int main() {

test01();

system("pause");

return 0;

}

```

#### 4.7.4 多態(tài)案例二-制作飲品

**案例描述:**

制作飲品的大致流程為:煮水 -? 沖泡 - 倒入杯中 - 加入輔料

利用多態(tài)技術(shù)實(shí)現(xiàn)本案例谢鹊,提供抽象制作飲品基類算吩,提供子類制作咖啡和茶葉

![1545985945198](assets/1545985945198.png)

**示例:**

```C++

//抽象制作飲品

class AbstractDrinking {

public:

//燒水

virtual void Boil() = 0;

//沖泡

virtual void Brew() = 0;

//倒入杯中

virtual void PourInCup() = 0;

//加入輔料

virtual void PutSomething() = 0;

//規(guī)定流程

void MakeDrink() {

Boil();

Brew();

PourInCup();

PutSomething();

}

};

//制作咖啡

class Coffee : public AbstractDrinking {

public:

//燒水

virtual void Boil() {

cout << "煮農(nóng)夫山泉!" << endl;

}

//沖泡

virtual void Brew() {

cout << "沖泡咖啡!" << endl;

}

//倒入杯中

virtual void PourInCup() {

cout << "將咖啡倒入杯中!" << endl;

}

//加入輔料

virtual void PutSomething() {

cout << "加入牛奶!" << endl;

}

};

//制作茶水

class Tea : public AbstractDrinking {

public:

//燒水

virtual void Boil() {

cout << "煮自來水!" << endl;

}

//沖泡

virtual void Brew() {

cout << "沖泡茶葉!" << endl;

}

//倒入杯中

virtual void PourInCup() {

cout << "將茶水倒入杯中!" << endl;

}

//加入輔料

virtual void PutSomething() {

cout << "加入枸杞!" << endl;

}

};

//業(yè)務(wù)函數(shù)

void DoWork(AbstractDrinking* drink) {

drink->MakeDrink();

delete drink;

}

void test01() {

DoWork(new Coffee);

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

DoWork(new Tea);

}

int main() {

test01();

system("pause");

return 0;

}

```

#### 4.7.5 虛析構(gòu)和純虛析構(gòu)

多態(tài)使用時,如果子類中有屬性開辟到堆區(qū)佃扼,那么父類指針在釋放時無法調(diào)用到子類的析構(gòu)代碼

解決方式:將父類中的析構(gòu)函數(shù)改為**虛析構(gòu)**或者**純虛析構(gòu)**

虛析構(gòu)和純虛析構(gòu)共性:

* 可以解決父類指針釋放子類對象

* 都需要有具體的函數(shù)實(shí)現(xiàn)

虛析構(gòu)和純虛析構(gòu)區(qū)別:

* 如果是純虛析構(gòu)偎巢,該類屬于抽象類,無法實(shí)例化對象

虛析構(gòu)語法:

`virtual ~類名(){}`

純虛析構(gòu)語法:

` virtual ~類名() = 0;`

`類名::~類名(){}`

**示例:**

```C++

class Animal {

public:

Animal()

{

cout << "Animal 構(gòu)造函數(shù)調(diào)用兼耀!" << endl;

}

virtual void Speak() = 0;

//析構(gòu)函數(shù)加上virtual關(guān)鍵字压昼,變成虛析構(gòu)函數(shù)

//virtual ~Animal()

//{

// cout << "Animal虛析構(gòu)函數(shù)調(diào)用求冷!" << endl;

//}

virtual ~Animal() = 0;

};

Animal::~Animal()

{

cout << "Animal 純虛析構(gòu)函數(shù)調(diào)用!" << endl;

}

//和包含普通純虛函數(shù)的類一樣窍霞,包含了純虛析構(gòu)函數(shù)的類也是一個抽象類匠题。不能夠被實(shí)例化。

class Cat : public Animal {

public:

Cat(string name)

{

cout << "Cat構(gòu)造函數(shù)調(diào)用官撼!" << endl;

m_Name = new string(name);

}

virtual void Speak()

{

cout << *m_Name <<? "小貓?jiān)谡f話!" << endl;

}

~Cat()

{

cout << "Cat析構(gòu)函數(shù)調(diào)用!" << endl;

if (this->m_Name != NULL) {

delete m_Name;

m_Name = NULL;

}

}

public:

string *m_Name;

};

void test01()

{

Animal *animal = new Cat("Tom");

animal->Speak();

//通過父類指針去釋放梧躺,會導(dǎo)致子類對象可能清理不干凈似谁,造成內(nèi)存泄漏

//怎么解決傲绣?給基類增加一個虛析構(gòu)函數(shù)

//虛析構(gòu)函數(shù)就是用來解決通過父類指針釋放子類對象

delete animal;

}

int main() {

test01();

system("pause");

return 0;

}

```

總結(jié):

? 1. 虛析構(gòu)或純虛析構(gòu)就是用來解決通過父類指針釋放子類對象

? 2. 如果子類中沒有堆區(qū)數(shù)據(jù),可以不寫為虛析構(gòu)或純虛析構(gòu)

? 3. 擁有純虛析構(gòu)函數(shù)的類也屬于抽象類

#### 4.7.6 多態(tài)案例三-電腦組裝

**案例描述:**

電腦主要組成部件為 CPU(用于計(jì)算)巩踏,顯卡(用于顯示)秃诵,內(nèi)存條(用于存儲)

將每個零件封裝出抽象基類,并且提供不同的廠商生產(chǎn)不同的零件塞琼,例如Intel廠商和Lenovo廠商

創(chuàng)建電腦類提供讓電腦工作的函數(shù)菠净,并且調(diào)用每個零件工作的接口

測試時組裝三臺不同的電腦進(jìn)行工作

**示例:**

```C++

#include<iostream>

using namespace std;

//抽象CPU類

class CPU

{

public:

//抽象的計(jì)算函數(shù)

virtual void calculate() = 0;

};

//抽象顯卡類

class VideoCard

{

public:

//抽象的顯示函數(shù)

virtual void display() = 0;

};

//抽象內(nèi)存條類

class Memory

{

public:

//抽象的存儲函數(shù)

virtual void storage() = 0;

};

//電腦類

class Computer

{

public:

Computer(CPU * cpu, VideoCard * vc, Memory * mem)

{

m_cpu = cpu;

m_vc = vc;

m_mem = mem;

}

//提供工作的函數(shù)

void work()

{

//讓零件工作起來,調(diào)用接口

m_cpu->calculate();

m_vc->display();

m_mem->storage();

}

//提供析構(gòu)函數(shù) 釋放3個電腦零件

~Computer()

{

//釋放CPU零件

if (m_cpu != NULL)

{

delete m_cpu;

m_cpu = NULL;

}

//釋放顯卡零件

if (m_vc != NULL)

{

delete m_vc;

m_vc = NULL;

}

//釋放內(nèi)存條零件

if (m_mem != NULL)

{

delete m_mem;

m_mem = NULL;

}

}

private:

CPU * m_cpu; //CPU的零件指針

VideoCard * m_vc; //顯卡零件指針

Memory * m_mem; //內(nèi)存條零件指針

};

//具體廠商

//Intel廠商

class IntelCPU :public CPU

{

public:

virtual void calculate()

{

cout << "Intel的CPU開始計(jì)算了彪杉!" << endl;

}

};

class IntelVideoCard :public VideoCard

{

public:

virtual void display()

{

cout << "Intel的顯卡開始顯示了毅往!" << endl;

}

};

class IntelMemory :public Memory

{

public:

virtual void storage()

{

cout << "Intel的內(nèi)存條開始存儲了!" << endl;

}

};

//Lenovo廠商

class LenovoCPU :public CPU

{

public:

virtual void calculate()

{

cout << "Lenovo的CPU開始計(jì)算了派近!" << endl;

}

};

class LenovoVideoCard :public VideoCard

{

public:

virtual void display()

{

cout << "Lenovo的顯卡開始顯示了攀唯!" << endl;

}

};

class LenovoMemory :public Memory

{

public:

virtual void storage()

{

cout << "Lenovo的內(nèi)存條開始存儲了!" << endl;

}

};

void test01()

{

//第一臺電腦零件

CPU * intelCpu = new IntelCPU;

VideoCard * intelCard = new IntelVideoCard;

Memory * intelMem = new IntelMemory;

cout << "第一臺電腦開始工作:" << endl;

//創(chuàng)建第一臺電腦

Computer * computer1 = new Computer(intelCpu, intelCard, intelMem);

computer1->work();

delete computer1;

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

cout << "第二臺電腦開始工作:" << endl;

//第二臺電腦組裝

Computer * computer2 = new Computer(new LenovoCPU, new LenovoVideoCard, new LenovoMemory);;

computer2->work();

delete computer2;

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

cout << "第三臺電腦開始工作:" << endl;

//第三臺電腦組裝

Computer * computer3 = new Computer(new LenovoCPU, new IntelVideoCard, new LenovoMemory);;

computer3->work();

delete computer3;

}

```

## 5 文件操作

程序運(yùn)行時產(chǎn)生的數(shù)據(jù)都屬于臨時數(shù)據(jù)渴丸,程序一旦運(yùn)行結(jié)束都會被釋放

通過**文件可以將數(shù)據(jù)持久化**

C++中對文件操作需要包含頭文件 ==&lt; fstream &gt;==

文件類型分為兩種:

1. **文本文件**? ? -? 文件以文本的**ASCII碼**形式存儲在計(jì)算機(jī)中

2. **二進(jìn)制文件** -? 文件以文本的**二進(jìn)制**形式存儲在計(jì)算機(jī)中侯嘀,用戶一般不能直接讀懂它們

操作文件的三大類:

1. ofstream:寫操作

2. ifstream: 讀操作

3. fstream : 讀寫操作

### 5.1文本文件

#### 5.1.1寫文件

? 寫文件步驟如下:

1. 包含頭文件?

? ? \#include <fstream\>

2. 創(chuàng)建流對象?

? ofstream ofs;

3. 打開文件

? ofs.open("文件路徑",打開方式);

4. 寫數(shù)據(jù)

? ofs << "寫入的數(shù)據(jù)";

5. 關(guān)閉文件

? ofs.close();


文件打開方式:

| 打開方式? ? | 解釋? ? ? ? ? ? ? ? ? ? ? |

| ----------- | -------------------------- |

| ios::in? ? | 為讀文件而打開文件? ? ? ? |

| ios::out? ? | 為寫文件而打開文件? ? ? ? |

| ios::ate? ? | 初始位置:文件尾? ? ? ? ? |

| ios::app? ? | 追加方式寫文件? ? ? ? ? ? |

| ios::trunc? | 如果文件存在先刪除,再創(chuàng)建 |

| ios::binary | 二進(jìn)制方式? ? ? ? ? ? ? ? |

**注意:** 文件打開方式可以配合使用谱轨,利用|操作符

**例如:**用二進(jìn)制方式寫文件 `ios::binary |? ios:: out`

**示例:**

```C++

#include <fstream>

void test01()

{

ofstream ofs;

ofs.open("test.txt", ios::out);

ofs << "姓名:張三" << endl;

ofs << "性別:男" << endl;

ofs << "年齡:18" << endl;

ofs.close();

}

int main() {

test01();

system("pause");

return 0;

}

```

總結(jié):

* 文件操作必須包含頭文件 fstream

* 讀文件可以利用 ofstream? 戒幔,或者fstream類

* 打開文件時候需要指定操作文件的路徑,以及打開方式

* 利用<<可以向文件中寫數(shù)據(jù)

* 操作完畢土童,要關(guān)閉文件

#### 5.1.2讀文件

讀文件與寫文件步驟相似诗茎,但是讀取方式相對于比較多

讀文件步驟如下:

1. 包含頭文件?

? ? \#include <fstream\>

2. 創(chuàng)建流對象?

? ifstream ifs;

3. 打開文件并判斷文件是否打開成功

? ifs.open("文件路徑",打開方式);

4. 讀數(shù)據(jù)

? 四種方式讀取

5. 關(guān)閉文件

? ifs.close();

**示例:**

```C++

#include <fstream>

#include <string>

void test01()

{

ifstream ifs;

ifs.open("test.txt", ios::in);

if (!ifs.is_open())

{

cout << "文件打開失敗" << endl;

return;

}

//第一種方式

//char buf[1024] = { 0 };

//while (ifs >> buf)

//{

// 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)

{

cout << c;

}

ifs.close();

}

int main() {

test01();

system("pause");

return 0;

}

```

總結(jié):

- 讀文件可以利用 ifstream? ,或者fstream類

- 利用is_open函數(shù)可以判斷文件是否打開成功

- close 關(guān)閉文件

### 5.2 二進(jìn)制文件

以二進(jìn)制的方式對文件進(jìn)行讀寫操作

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

#### 5.2.1 寫文件

二進(jìn)制方式寫文件主要利用流對象調(diào)用成員函數(shù)write

函數(shù)原型 :`ostream& write(const char * buffer,int len);`

參數(shù)解釋:字符指針buffer指向內(nèi)存中一段存儲空間献汗。len是讀寫的字節(jié)數(shù)

**示例:**

```C++

#include <fstream>

#include <string>

class Person

{

public:

char m_Name[64];

int m_Age;

};

//二進(jìn)制文件? 寫文件

void test01()

{

//1敢订、包含頭文件

//2、創(chuàng)建輸出流對象

ofstream ofs("person.txt", ios::out | ios::binary);

//3雀瓢、打開文件

//ofs.open("person.txt", ios::out | ios::binary);

Person p = {"張三"? , 18};

//4枢析、寫文件

ofs.write((const char *)&p, sizeof(p));

//5、關(guān)閉文件

ofs.close();

}

int main() {

test01();

system("pause");

return 0;

}

```

總結(jié):

* 文件輸出流對象 可以通過write函數(shù)刃麸,以二進(jìn)制方式寫數(shù)據(jù)

#### 5.2.2 讀文件

二進(jìn)制方式讀文件主要利用流對象調(diào)用成員函數(shù)read

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

參數(shù)解釋:字符指針buffer指向內(nèi)存中一段存儲空間醒叁。len是讀寫的字節(jié)數(shù)

示例:

```C++

#include <fstream>

#include <string>

class Person

{

public:

char m_Name[64];

int m_Age;

};

void test01()

{

ifstream ifs("person.txt", ios::in | ios::binary);

if (!ifs.is_open())

{

cout << "文件打開失敗" << endl;

}

Person p;

ifs.read((char *)&p, sizeof(p));

cout << "姓名: " << p.m_Name << " 年齡: " << p.m_Age << endl;

}

int main() {

test01();

system("pause");

return 0;

}

```

- 文件輸入流對象 可以通過read函數(shù),以二進(jìn)制方式讀數(shù)據(jù)

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
禁止轉(zhuǎn)載,如需轉(zhuǎn)載請通過簡信或評論聯(lián)系作者把沼。
  • 序言:七十年代末啊易,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子饮睬,更是在濱河造成了極大的恐慌租谈,老刑警劉巖,帶你破解...
    沈念sama閱讀 212,599評論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件捆愁,死亡現(xiàn)場離奇詭異割去,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)昼丑,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,629評論 3 385
  • 文/潘曉璐 我一進(jìn)店門呻逆,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人菩帝,你說我怎么就攤上這事咖城。” “怎么了呼奢?”我有些...
    開封第一講書人閱讀 158,084評論 0 348
  • 文/不壞的土叔 我叫張陵宜雀,是天一觀的道長。 經(jīng)常有香客問我握础,道長辐董,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,708評論 1 284
  • 正文 為了忘掉前任弓候,我火速辦了婚禮郎哭,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘菇存。我一直安慰自己夸研,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,813評論 6 386
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般苔埋。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上姐扮,一...
    開封第一講書人閱讀 50,021評論 1 291
  • 那天,我揣著相機(jī)與錄音衣吠,去河邊找鬼茶敏。 笑死,一個胖子當(dāng)著我的面吹牛缚俏,可吹牛的內(nèi)容都是我干的惊搏。 我是一名探鬼主播贮乳,決...
    沈念sama閱讀 39,120評論 3 410
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼恬惯!你這毒婦竟也來了向拆?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,866評論 0 268
  • 序言:老撾萬榮一對情侶失蹤酪耳,失蹤者是張志新(化名)和其女友劉穎浓恳,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體碗暗,經(jīng)...
    沈念sama閱讀 44,308評論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡颈将,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,633評論 2 327
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了讹堤。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片吆鹤。...
    茶點(diǎn)故事閱讀 38,768評論 1 341
  • 序言:一個原本活蹦亂跳的男人離奇死亡厨疙,死狀恐怖洲守,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情沾凄,我是刑警寧澤梗醇,帶...
    沈念sama閱讀 34,461評論 4 333
  • 正文 年R本政府宣布,位于F島的核電站撒蟀,受9級特大地震影響叙谨,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜保屯,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 40,094評論 3 317
  • 文/蒙蒙 一手负、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧姑尺,春花似錦竟终、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,850評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至柄粹,卻和暖如春喘鸟,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背驻右。 一陣腳步聲響...
    開封第一講書人閱讀 32,082評論 1 267
  • 我被黑心中介騙來泰國打工什黑, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人堪夭。 一個月前我還...
    沈念sama閱讀 46,571評論 2 362
  • 正文 我出身青樓愕把,卻偏偏與公主長得像凯力,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子礼华,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,666評論 2 350