主頁 > 軟體設計 > 熬夜爆肝!C++核心進階知識點匯總整理【萬字干貨預警 建議收藏】

熬夜爆肝!C++核心進階知識點匯總整理【萬字干貨預警 建議收藏】

2021-07-27 08:09:17 軟體設計

前言

前段時間有粉絲問我,大一結束c++剛學完,不知道自己目前學得怎么樣?要掌握的知識點有沒有都弄懂了?是否基礎入門了?

前幾天已經整理過C++基礎入門知識點,沒看過的可以看看喔!熬夜爆肝!C++基礎入門大合集【萬字干貨預警 建議收藏】

今天繼續整理C++進階篇知識點,一起來看看吧~

在這里插入圖片描述

目錄

  • 前言
  • C++核心編程
    • 1 記憶體磁區模型
      • 1.1 程式運行前
      • 1.2 程式運行后
      • 1.3 new運算子
    • 2 參考
      • 2.1 參考的基本使用
      • 2.2 參考注意事項
      • 2.3 參考做函式引數
      • 2.4 參考做函式回傳值
      • 2.5 參考的本質
      • 2.6 常量參考
    • 3 函式提高
      • 3.1 函式默認引數
      • 3.2 函式占位引數
      • 3.3 函式多載
        • 3.3.1 函式多載概述
    • 4 類和物件
      • 4.1 封裝
        • 4.1.1 封裝的意義
        • 4.1.2 struct和class區別
        • 4.1.3 成員屬性設定為私有
      • 4.2 物件的初始化和清理
        • 4.2.1 建構式和解構式
        • 4.2.2 建構式的分類及呼叫
        • 4.2.3 拷貝建構式呼叫時機
        • 4.2.4 建構式呼叫規則
        • 4.2.5 深拷貝與淺拷貝
        • 4.2.6 初始化串列
        • 4.2.7 類物件作為類成員
        • 4.2.8 靜態成員
      • 4.3 C++物件模型和this指標
        • 4.3.1 成員變數和成員函式分開存盤
        • 4.3.2 this指標概念
        • 4.3.3 空指標訪問成員函式
        • 4.3.4 const修飾成員函式
      • 4.4 友元
        • 4.4.1 全域函式做友元
        • 4.4.2 類做友元
        • 4.4.3 成員函式做友元
      • 4.6 繼承
        • 4.6.1 繼承
        • 4.6.2 繼承方式
        • 4.6.3 繼承中構造和析構順序
        • 4.6.4 繼承同名成員處理方式
        • 4.6.5 繼承同名靜態成員處理方式
        • 4.6.6 多繼承語法
      • 4.7 多型
        • 4.7.1 多型的基本概念
        • 4.7.2 純虛函式和抽象類
        • 4.7.3 虛析構和純虛析構
    • 5 檔案操作
      • 5.1文本檔案
        • 5.1.1寫檔案
        • 5.1.2讀檔案
      • 5.2 二進制檔案
        • 5.2.1 寫檔案
        • 5.2.2 讀檔案

C++核心編程

主要針對C++面向物件編程技術做詳細講解,探討C++的核心和精髓,

1 記憶體磁區模型

C++程式在執行時,將記憶體大方向劃分為4個區域

  • 代碼區:存放函式體的二進制代碼,由作業系統進行管理的
  • 全域區:存放全域變數和靜態變數以及常量
  • 堆疊區:由編譯器自動分配釋放, 存放函式的引數值,區域變數等
  • 堆區:由程式員分配和釋放,若程式員不釋放,程式結束時由作業系統回收

記憶體四區意義:

不同區域存放的資料,賦予不同的生命周期, 給我們更大的靈活編程

1.1 程式運行前

? 在程式編譯后,生成了exe可執行程式,未執行該程式前分為兩個區域

? 代碼區:

? 存放 CPU 執行的機器指令

? 代碼區是共享的,共享的目的是對于頻繁被執行的程式,只需要在記憶體中有一份代碼即可

? 代碼區是只讀的,使其只讀的原因是防止程式意外地修改了它的指令

? 全域區:

? 全域變數和靜態變數存放在此.

? 全域區還包含了常量區, 字串常量和其他常量也存放在此.

? 該區域的資料在程式結束后由作業系統釋放.

示例:

//全域變數
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;

	//靜態變數
	static int s_a = 10;
	static int s_b = 10;

	cout << "靜態變數s_a地址為: " << (int)&s_a << endl;
	cout << "靜態變數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;
}

列印結果:

在這里插入圖片描述

  • C++中在程式運行前分為全域區和代碼區
  • 代碼區特點是共享和只讀
  • 全域區中存放全域變數、靜態變數、常量
  • 常量區中存放 const修飾的全域常量 和 字串常量

1.2 程式運行后

? 堆疊區:

? 由編譯器自動分配釋放, 存放函式的引數值,區域變數等

? 注意事項:不要回傳區域變數的地址,堆疊區開辟的資料由編譯器自動釋放

示例:

int * func()
{
	int a = 10;
	return &a;
}

int main() {

	int *p = func();

	cout << *p << endl;
	cout << *p << endl;

	system("pause");

	return 0;
}

? 堆區:

? 由程式員分配釋放,若程式員不釋放,程式結束時由作業系統回收

? 在C++中主要利用new在堆區開辟記憶體

示例:

int* func()
{
	int* a = new int(10);
	return a;
}

int main() {

	int *p = func();

	cout << *p << endl;
	cout << *p << endl;
    
	system("pause");

	return 0;
}

總結:

堆區資料由程式員管理開辟和釋放

堆區資料利用new關鍵字進行開辟記憶體

1.3 new運算子

? C++中利用new運算子在堆區開辟資料

? 堆區開辟的資料,由程式員手動開辟,手動釋放,釋放利用運算子 delete

? 語法:new 資料型別

? 利用new創建的資料,會回傳該資料對應的型別的指標

示例1: 基本語法

int* func()
{
	int* a = new int(10);
	return a;
}

int main() {

	int *p = func();

	cout << *p << endl;
	cout << *p << endl;

	//利用delete釋放堆區資料
	delete p;

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

	system("pause");

	return 0;
}

示例2:開辟陣列

//堆區開辟陣列
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;
	}
	//釋放陣列 delete 后加 []
	delete[] arr;

	system("pause");

	return 0;
}

2 參考

2.1 參考的基本使用

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

語法: 資料型別 &別名 = 原名

示例:

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 參考注意事項

  • 參考必須初始化
  • 參考在初始化后,不可以改變

示例:

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 參考做函式引數

**作用:**函式傳參時,可以利用參考的技術讓形參修飾實參

**優點:**可以簡化指標修改實參

示例:

//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;
}

2.4 參考做函式回傳值

參考是可以作為函式的回傳值存在的,不要回傳區域變數參考

用法:函式呼叫作為左值

//回傳區域變數參考
int& test01() {
	int a = 10; //區域變數
	return a;
}

//回傳靜態變數參考
int& test02() {
	static int a = 20;
	return a;
}

int main() {

	//不能回傳區域變數的參考
	int& ref = test01();
	cout << "ref = " << ref << endl;
	cout << "ref = " << ref << endl;

	//如果函式做左值,那么必須回傳參考
	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 參考的本質

本質:參考的本質在c++內部實作是一個指標常量.

C++推薦用參考技術,因為語法方便,參考本質是指標常量,但是所有的指標操作編譯器都幫我們做了

//發現是參考,轉換為 int* const ref = &a;
void func(int& ref){
	ref = 100; // ref是參考,轉換為*ref = 100
}
int main(){
	int a = 10;
    
    //自動轉換為 int* const ref = &a; 指標常量是指標指向不可改,也說明為什么參考不可更改
	int& ref = a; 
	ref = 20; //內部發現ref是參考,自動幫我們轉換為: *ref = 20;
    
	cout << "a:" << a << endl;
	cout << "ref:" << ref << endl;
    
	func(a);
	return 0;
}

2.6 常量參考

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

在函式形參串列中,可以加const修飾形參,防止形參改變實參

//參考使用的場景,通常用來修飾形參
void showValue(const int& v) {
	//v += 10;
	cout << v << endl;
}

int main() {

	//int& ref = 10;  參考本身需要一個合法的記憶體空間,因此這行錯誤
	//加入const就可以了,編譯器優化代碼,int temp = 10; const int& ref = temp;
	const int& ref = 10;

	//ref = 100;  //加入const后不可以修改變數
	cout << ref << endl;

	//函式中利用常量參考防止誤操作修改實參
	int a = 10;
	showValue(a);

	system("pause");

	return 0;
}

3 函式提高

3.1 函式默認引數

在C++中,函式的形參串列中的形參是可以有默認值的,

語法:回傳值型別 函式名 (引數= 默認值){}

int func(int a, int b = 10, int c = 10) {
	return a + b + c;
}

//1. 如果某個位置引數有默認值,那么從這個位置往后,從左向右,必須都要有默認值
//2. 如果函式宣告有默認值,函式實作的時候就不能有默認引數
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 函式占位引數

C++中函式的形參串列里可以有占位引數,用來做占位,呼叫函式時必須填補該位置

回傳值型別 函式名 (資料型別){}

//函式占位引數 ,占位引數也可以有默認引數
void func(int a, int) {
	cout << "this is func" << endl;
}

int main() {

	func(10,10); //占位引數必須填補

	system("pause");

	return 0;
}

3.3 函式多載

3.3.1 函式多載概述

函式名可以相同,提高復用性

函式多載滿足條件:

  • 同一個作用域下
  • 函式名稱相同
  • 函式引數型別不同 或者 個數不同 或者 順序不同

函式的回傳值不可以作為函式多載的條件

//函式多載需要函式都在同一個作用域下
void func()
{
	cout << "func 的呼叫!" << endl;
}
void func(int a)
{
	cout << "func (int a) 的呼叫!" << endl;
}
void func(double a)
{
	cout << "func (double a)的呼叫!" << endl;
}
void func(int a ,double b)
{
	cout << "func (int a ,double b) 的呼叫!" << endl;
}
void func(double a ,int b)
{
	cout << "func (double a ,int b)的呼叫!" << endl;
}

//函式回傳值不可以作為函式多載條件
//int func(double a, int b)
//{
//	cout << "func (double a ,int b)的呼叫!" << endl;
//}

int main() {

	func();
	func(10);
	func(3.14);
	func(10,3.14);
	func(3.14 , 10);
	
	system("pause");

	return 0;
}

4 類和物件

C++面向物件的三大特性為:封裝、繼承、多型

4.1 封裝

4.1.1 封裝的意義

封裝是C++面向物件三大特性之一

封裝的意義:

  • 將屬性和行為作為一個整體,表現生活中的事物
  • 將屬性和行為加以權限控制

封裝意義一:

? 在設計類的時候,屬性和行為寫在一起,表現事物

語法: class 類名{ 訪問權限: 屬性 / 行為 };

**示例1:**設計一個圓類,求圓的周長

示例代碼:

//圓周率
const double PI = 3.14;

//1、封裝的意義
//將屬性和行為作為一個整體,用來表現生活中的事物

//封裝一個圓類,求圓的周長
//class代表設計一個類,后面跟著的是類名
class Circle
{
public:  //訪問權限  公共的權限

	//屬性
	int m_r;//半徑

	//行為
	//獲取到圓的周長
	double calculateZC()
	{
		//2 * pi  * r
		//獲取圓的周長
		return  2 * PI * m_r;
	}
};

int main() {

	//通過圓類,創建圓的物件
	// c1就是一個具體的圓
	Circle c1;
	c1.m_r = 10; //給圓物件的半徑 進行賦值操作

	//2 * pi * 10 = = 62.8
	cout << "圓的周長為: " << c1.calculateZC() << endl;

	system("pause");

	return 0;
}

封裝意義二:

類在設計時,可以把屬性和行為放在不同的權限下,加以控制

訪問權限有三種:

  1. public 公共權限
  2. protected 保護權限
  3. private 私有權限
//三種權限
//公共權限  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;
	}
};

int main() {

	Person p;
	p.m_Name = "李四";
	//p.m_Car = "奔馳";  //保護權限類外訪問不到
	//p.m_Password = 123; //私有權限類外訪問不到

	system("pause");

	return 0;
}

4.1.2 struct和class區別

在C++中 struct和class唯一的區別就在于 默認的訪問權限不同

區別:

  • struct 默認權限為公共
  • class 默認權限為私有
class C1
{
	int  m_A; //默認是私有權限
};

struct C2
{
	int m_A;  //默認是公共權限
};

int main() {

	C1 c1;
	c1.m_A = 10; //錯誤,訪問權限是私有

	C2 c2;
	c2.m_A = 10; //正確,訪問權限是公共

	system("pause");

	return 0;
}

4.1.3 成員屬性設定為私有

  • 將所有成員屬性設定為私有,可以自己控制讀寫權限
  • 對于寫權限,我們可以檢測資料的有效性
class Person {
public:

	//姓名設定可讀可寫
	void setName(string name) {
		m_Name = name;
	}
	string getName()
	{
		return m_Name;
	}


	//獲取年齡 
	int getAge() {
		return m_Age;
	}
	//設定年齡
	void setAge(int age) {
		if (age < 0 || age > 150) {
			cout << "你個老妖精!" << endl;
			return;
		}
		m_Age = age;
	}

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

private:
	string m_Name; //可讀可寫  姓名
	
	int m_Age; //只讀  年齡

	string m_Lover; //只寫  情人
};


int main() {

	Person p;
	//姓名設定
	p.setName("張三");
	cout << "姓名: " << p.getName() << endl;

	//年齡設定
	p.setAge(50);
	cout << "年齡: " << p.getAge() << endl;

	//情人設定
	p.setLover("蒼井");
	//cout << "情人: " << p.m_Lover << endl;  //只寫屬性,不可以讀取

	system("pause");

	return 0;
}

4.2 物件的初始化和清理

4.2.1 建構式和解構式

物件的初始化和清理也是兩個非常重要的安全問題

? 一個物件或者變數沒有初始狀態,對其使用后果是未知

? 同樣的使用完一個物件或變數,沒有及時清理,也會造成一定的安全問題

c++利用了建構式解構式解決上述問題,這兩個函式將會被編譯器自動呼叫,完成物件初始化和清理作業,

物件的初始化和清理作業是編譯器強制要我們做的事情,因此如果我們不提供構造和析構,編譯器會提供

編譯器提供的建構式和解構式是空實作,

  • 建構式:主要作用在于創建物件時為物件的成員屬性賦值,建構式由編譯器自動呼叫,無須手動呼叫,
  • 解構式:主要作用在于物件銷毀前系統自動呼叫,執行一些清理作業,

建構式語法:類名(){}

  1. 建構式,沒有回傳值也不寫void
  2. 函式名稱與類名相同
  3. 建構式可以有引數,因此可以發生多載
  4. 程式在呼叫物件時候會自動呼叫構造,無須手動呼叫,而且只會呼叫一次

解構式語法: ~類名(){}

  1. 解構式,沒有回傳值也不寫void
  2. 函式名稱與類名相同,在名稱前加上符號 ~
  3. 解構式不可以有引數,因此不可以發生多載
  4. 程式在物件銷毀前會自動呼叫析構,無須手動呼叫,而且只會呼叫一次

4.2.2 建構式的分類及呼叫

兩種分類方式:

? 按引數分為: 有參構造和無參構造

? 按型別分為: 普通構造和拷貝構造

三種呼叫方式:

? 括號法

? 顯示法

? 隱式轉換法

//1、建構式分類
// 按照引數分類分為 有參和無參構造   無參又稱為默認建構式
// 按照型別分類分為 普通構造和拷貝構造

class Person {
public:
	//無參(默認)建構式
	Person() {
		cout << "無參建構式!" << endl;
	}
	//有參建構式
	Person(int a) {
		age = a;
		cout << "有參建構式!" << endl;
	}
	//拷貝建構式
	Person(const Person& p) {
		age = p.age;
		cout << "拷貝建構式!" << endl;
	}
	//解構式
	~Person() {
		cout << "解構式!" << endl;
	}
public:
	int age;
};

//2、建構式的呼叫
//呼叫無參建構式
void test01() {
	Person p; //呼叫無參建構式
}

//呼叫有參的建構式
void test02() {

	//2.1  括號法,常用
	Person p1(10);
	//注意1:呼叫無參建構式不能加括號,如果加了編譯器認為這是一個函式宣告
	//Person p2();

	//2.2 顯式法
	Person p2 = Person(10); 
	Person p3 = Person(p2);
	//Person(10)單獨寫就是匿名物件  當前行結束之后,馬上析構

	//2.3 隱式轉換法
	Person p4 = 10; // Person p4 = Person(10); 
	Person p5 = p4; // Person p5 = Person(p4); 

	//注意2:不能利用 拷貝建構式 初始化匿名物件 編譯器認為是物件宣告
	//Person p5(p4);
}

int main() {

	test01();
	//test02();

	system("pause");

	return 0;
}

4.2.3 拷貝建構式呼叫時機

C++中拷貝建構式呼叫時機通常有三種情況

  • 使用一個已經創建完畢的物件來初始化一個新物件
  • 值傳遞的方式給函式引數傳值
  • 以值方式回傳區域物件
class Person {
public:
	Person() {
		cout << "無參建構式!" << endl;
		mAge = 0;
	}
	Person(int age) {
		cout << "有參建構式!" << endl;
		mAge = age;
	}
	Person(const Person& p) {
		cout << "拷貝建構式!" << endl;
		mAge = p.mAge;
	}
	//解構式在釋放記憶體之前呼叫
	~Person() {
		cout << "解構式!" << endl;
	}
public:
	int mAge;
};

//1. 使用一個已經創建完畢的物件來初始化一個新物件
void test01() {

	Person man(100); //p物件已經創建完畢
	Person newman(man); //呼叫拷貝建構式
	Person newman2 = man; //拷貝構造

	//Person newman3;
	//newman3 = man; //不是呼叫拷貝建構式,賦值操作
}

//2. 值傳遞的方式給函式引數傳值
//相當于Person p1 = p;
void doWork(Person p1) {}
void test02() {
	Person p; //無參建構式
	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 建構式呼叫規則

默認情況下,c++編譯器至少給一個類添加3個函式

1.默認建構式(無參,函式體為空)

2.默認解構式(無參,函式體為空)

3.默認拷貝建構式,對屬性進行值拷貝

建構式呼叫規則如下:

  • 如果用戶定義有參建構式,c++不在提供默認無參構造,但是會提供默認拷貝構造

  • 如果用戶定義拷貝建構式,c++不會再提供其他建構式

4.2.5 深拷貝與淺拷貝

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

深拷貝:在堆區重新申請空間,進行拷貝操作

如果屬性有在堆區開辟的,一定要自己提供拷貝建構式,防止淺拷貝帶來的問題

class Person {
public:
	//無參(默認)建構式
	Person() {
		cout << "無參建構式!" << endl;
	}
	//有參建構式
	Person(int age ,int height) {
		
		cout << "有參建構式!" << endl;

		m_age = age;
		m_height = new int(height);
		
	}
	//拷貝建構式  
	Person(const Person& p) {
		cout << "拷貝建構式!" << endl;
		//如果不利用深拷貝在堆區創建新記憶體,會導致淺拷貝帶來的重復釋放堆區問題
		m_age = p.m_age;
		m_height = new int(*p.m_height);
		
	}

	//解構式
	~Person() {
		cout << "解構式!" << 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;
}

4.2.6 初始化串列

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

建構式():屬性1(值1),屬性2(值2)… {}

class Person {
public:

	傳統方式初始化
	//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++類中的成員可以是另一個類的物件,我們稱該成員為 物件成員

例如:

class A {}class B{    A a;}

B類中有物件A作為成員,A為物件成員

class Phone
{
public:
	Phone(string name)
	{
		m_PhoneName = name;
		cout << "Phone構造" << endl;
	}

	~Phone()
	{
		cout << "Phone析構" << endl;
	}

	string m_PhoneName;

};


class Person
{
public:

	//初始化串列可以告訴編譯器呼叫哪一個建構式
	Person(string name, string pName) :m_Name(name), m_Phone(pName)
	{
		cout << "Person構造" << endl;
	}

	~Person()
	{
		cout << "Person析構" << endl;
	}

	void playGame()
	{
		cout << m_Name << " 使用" << m_Phone.m_PhoneName << " 牌手機! " << endl;
	}

	string m_Name;
	Phone m_Phone;

};
void test01()
{
	//當類中成員是其他類物件時,我們稱該成員為 物件成員
	//構造的順序是 :先呼叫物件成員的構造,再呼叫本類構造
	//析構順序與構造相反
	Person p("張三" , "蘋果X");
	p.playGame();

}


int main() {

	test01();

	system("pause");

	return 0;
}

4.2.8 靜態成員

靜態成員就是在成員變數和成員函式前加上關鍵字static,稱為靜態成員

靜態成員分為:

  • 靜態成員變數
    • 所有物件共享同一份資料
    • 在編譯階段分配記憶體
    • 類內宣告,類外初始化
  • 靜態成員函式
    • 所有物件共享同一個函式
    • 靜態成員函式只能訪問靜態成員變數

靜態成員變數:

class Person
{
	
public:

	static int m_A; //靜態成員變數

	//靜態成員變數特點:
	//1 在編譯階段分配記憶體
	//2 類內宣告,類外初始化
	//3 所有物件共享同一份資料

private:
	static int m_B; //靜態成員變數也是有訪問權限的
};
int Person::m_A = 10;
int Person::m_B = 10;

void test01()
{
	//靜態成員變數兩種訪問方式

	//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; //共享同一份資料
	cout << "p2.m_A = " << p2.m_A << endl;

	//2、通過類名
	cout << "m_A = " << Person::m_A << endl;


	//cout << "m_B = " << Person::m_B << endl; //私有權限訪問不到
}

int main() {

	test01();

	system("pause");

	return 0;
}

靜態成員函式:

class Person
{

public:

	//靜態成員函式特點:
	//1 程式共享一個函式
	//2 靜態成員函式只能訪問靜態成員變數
	
	static void func()
	{
		cout << "func呼叫" << endl;
		m_A = 100;
		//m_B = 100; //錯誤,不可以訪問非靜態成員變數
	}

	static int m_A; //靜態成員變數
	int m_B; // 
private:

	//靜態成員函式也是有訪問權限的
	static void func2()
	{
		cout << "func2呼叫" << endl;
	}
};
int Person::m_A = 10;


void test01()
{
	//靜態成員變數兩種訪問方式

	//1、通過物件
	Person p1;
	p1.func();

	//2、通過類名
	Person::func();


	//Person::func2(); //私有權限訪問不到
}

int main() {

	test01();

	system("pause");

	return 0;
}

4.3 C++物件模型和this指標

4.3.1 成員變數和成員函式分開存盤

在C++中,類內的成員變數和成員函式分開存盤

只有非靜態成員變數才屬于類的物件上

class Person {
public:
	Person() {
		mA = 0;
	}
	//非靜態成員變數占物件空間
	int mA;
	//靜態成員變數不占物件空間
	static int mB; 
	//函式也不占物件空間,所有函式共享一個函式實體
	void func() {
		cout << "mA:" << this->mA << endl;
	}
	//靜態成員函式也不占物件空間
	static void sfunc() {
	}
};

int main() {

	cout << sizeof(Person) << endl;

	system("pause");

	return 0;
}

4.3.2 this指標概念

通過上面我們知道在C++中成員變數和成員函式是分開存盤的

每一個非靜態成員函式只會誕生一份函式實體,也就是說多個同型別的物件會共用一塊代碼

c++通過提供特殊的物件指標,this指標,解決上述問題,this指標指向被呼叫的成員函式所屬的物件

this指標是隱含每一個非靜態成員函式內的一種指標

this指標不需要定義,直接使用即可

this指標的用途:

  • 當形參和成員變數同名時,可用this指標來區分
  • 在類的非靜態成員函式中回傳物件本身,可使用return *this

4.3.3 空指標訪問成員函式

C++中空指標也是可以呼叫成員函式的,但是也要注意有沒有用到this指標

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

4.3.4 const修飾成員函式

常函式:

  • 成員函式后加const后我們稱為這個函式為常函式
  • 常函式內不可以修改成員屬性
  • 成員屬性宣告時加關鍵字mutable后,在常函式中依然可以修改

常物件:

  • 宣告物件前加const稱該物件為常物件
  • 常物件只能呼叫常函式
class Person {
public:
	Person() {
		m_A = 0;
		m_B = 0;
	}

	//this指標的本質是一個指標常量,指標的指向不可修改
	//如果想讓指標指向的值也不可以修改,需要宣告常函式
	void ShowPerson() const {
		//const Type* const pointer;
		//this = NULL; //不能修改指標的指向 Person* const this;
		//this->mA = 100; //但是this指標指向的物件的資料是可以修改的

		//const修飾成員函式,表示指標指向的記憶體空間的資料不能修改,除了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修飾成員變數

	//常物件訪問成員函式
	person.MyFunc(); //常物件不能呼叫const的函式

}

int main() {

	test01();

	system("pause");

	return 0;
}

4.4 友元

在程式里,有些私有屬性 也想讓類外特殊的一些函式或者類進行訪問,就需要用到友元的技術

友元的目的就是讓一個函式或者類 訪問另一個類中私有成員

友元的關鍵字為 friend

友元的三種實作

  • 全域函式做友元
  • 類做友元
  • 成員函式做友元

4.4.1 全域函式做友元

class Building
{
	//告訴編譯器 goodGay全域函式 是 Building類的好朋友,可以訪問類中的私有內容
	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 類做友元

class Building;
class goodGay
{
public:

	goodGay();
	void visit();

private:
	Building *building;
};


class Building
{
	//告訴編譯器 goodGay類是Building類的好朋友,可以訪問到Building類中私有內容
	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 成員函式做友元

class Building;
class goodGay
{
public:

	goodGay();
	void visit(); //只讓visit函式作為Building的好朋友,可以發訪問Building中私有內容
	void visit2(); 

private:
	Building *building;
};


class Building
{
	//告訴編譯器  goodGay類中的visit成員函式 是Building好朋友,可以訪問私有內容
	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.6 繼承

繼承是面向物件三大特性之一

我們發現,定義這些類時,下級別的成員除了擁有上一級的共性,還有自己的特性,

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

4.6.1 繼承

繼承的好處:可以減少重復的代碼

class A : public B;

A 類稱為子類 或 派生類

B 類稱為父類 或 基類

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

一類是從基類繼承過來的,一類是自己增加的成員,

從基類繼承過過來的表現其共性,而新增的成員體現了其個性,

4.6.2 繼承方式

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

繼承方式一共有三種:

  • 公共繼承
  • 保護繼承
  • 私有繼承
class Base1
{
public: 
	int m_A;
protected:
	int m_B;
private:
	int m_C;
};

//公共繼承
class Son1 :public Base1
{
public:
	void func()
	{
		m_A; //可訪問 public權限
		m_B; //可訪問 protected權限
		//m_C; //不可訪問
	}
};

void myClass()
{
	Son1 s1;
	s1.m_A; //其他類只能訪問到公共權限
}

//保護繼承
class Base2
{
public:
	int m_A;
protected:
	int m_B;
private:
	int m_C;
};
class Son2:protected Base2
{
public:
	void func()
	{
		m_A; //可訪問 protected權限
		m_B; //可訪問 protected權限
		//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權限
		m_B; //可訪問 private權限
		//m_C; //不可訪問
	}
};
class GrandSon3 :public Son3
{
public:
	void func()
	{
		//Son3是私有繼承,所以繼承Son3的屬性在GrandSon3中都無法訪問到
		//m_A;
		//m_B;
		//m_C;
	}
};

4.6.3 繼承中構造和析構順序

子類繼承父類后,當創建子類物件,也會呼叫父類的建構式

繼承中 先呼叫父類建構式,再呼叫子類建構式,析構順序與構造相反

class Base 
{
public:
	Base()
	{
		cout << "Base建構式!" << endl;
	}
	~Base()
	{
		cout << "Base解構式!" << endl;
	}
};

class Son : public Base
{
public:
	Son()
	{
		cout << "Son建構式!" << endl;
	}
	~Son()
	{
		cout << "Son解構式!" << endl;
	}

};


void test01()
{
	//繼承中 先呼叫父類建構式,再呼叫子類建構式,析構順序與構造相反
	Son s;
}

int main() {

	test01();

	system("pause");

	return 0;
}

4.6.4 繼承同名成員處理方式

  1. 子類物件可以直接訪問到子類中同名成員
  2. 子類物件加作用域可以訪問到父類同名成員
  3. 當子類與父類擁有同名的成員函式,子類會隱藏父類中同名成員函式,加作用域可以訪問到父類中同名函式
class Base {
public:
	Base()
	{
		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 : public Base {
public:
	Son()
	{
		m_A = 200;
	}

	//當子類與父類擁有同名的成員函式,子類會隱藏父類中所有版本的同名成員函式
	//如果想訪問父類中被隱藏的同名成員函式,需要加父類的作用域
	void func()
	{
		cout << "Son - func()呼叫" << 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;
}

4.6.5 繼承同名靜態成員處理方式

靜態成員和非靜態成員出現同名,處理方式一致

  • 訪問子類同名成員 直接訪問即可
  • 訪問父類同名成員 需要加作用域

同名靜態成員處理方式和非靜態處理方式一樣,只不過有兩種訪問的方式(通過物件 和 通過類名)

4.6.6 多繼承語法

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

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

多繼承可能會引發父類中有同名成員出現,需要加作用域區分

C++實際開發中不建議用多繼承

class Base1 {
public:
	Base1()
	{
		m_A = 100;
	}
public:
	int m_A;
};

class Base2 {
public:
	Base2()
	{
		m_A = 200;  //開始是m_B 不會出問題,但是改為mA就會出現不明確
	}
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;
};


//多繼承容易產生成員同名的情況
//通過使用類名作用域可以區分呼叫哪一個基類的成員
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;
}

4.7 多型

4.7.1 多型的基本概念

多型是C++面向物件三大特性之一

多型分為兩類

  • 靜態多型: 函式多載 和 運算子多載屬于靜態多型,復用函式名
  • 動態多型: 派生類和虛函式實作運行時多型

靜態多型和動態多型區別:

  • 靜態多型的函式地址早系結 - 編譯階段確定函式地址
  • 動態多型的函式地址晚系結 - 運行階段確定函式地址
class Animal
{
public:
	//Speak函式就是虛函式
	//函式前面加上virtual關鍵字,變成虛函式,那么編譯器在編譯的時候就不能確定函式呼叫了,
	virtual void speak()
	{
		cout << "動物在說話" << endl;
	}
};

class Cat :public Animal
{
public:
	void speak()
	{
		cout << "小貓在說話" << endl;
	}
};

class Dog :public Animal
{
public:

	void speak()
	{
		cout << "小狗在說話" << endl;
	}

};
//我們希望傳入什么物件,那么就呼叫什么物件的函式
//如果函式地址在編譯階段就能確定,那么靜態聯編
//如果函式地址在運行階段才能確定,就是動態聯編

void DoSpeak(Animal & animal)
{
	animal.speak();
}
//
//多型滿足條件: 
//1、有繼承關系
//2、子類重寫父類中的虛函式
//多型使用:
//父類指標或參考指向子類物件

void test01()
{
	Cat cat;
	DoSpeak(cat);


	Dog dog;
	DoSpeak(dog);
}


int main() {

	test01();

	system("pause");

	return 0;
}

總結:

多型滿足條件

  • 有繼承關系
  • 子類重寫父類中的虛函式

多型使用條件

  • 父類指標或參考指向子類物件

重寫:函式回傳值型別 函式名 引數串列 完全一致稱為重寫

4.7.2 純虛函式和抽象類

在多型中,通常父類中虛函式的實作是毫無意義的,主要都是呼叫子類重寫的內容

因此可以將虛函式改為純虛函式

純虛函式語法:virtual 回傳值型別 函式名 (引數串列)= 0 ;

當類中有了純虛函式,這個類也稱為抽象類

抽象類特點

  • 無法實體化物件
  • 子類必須重寫抽象類中的純虛函式,否則也屬于抽象類
class Base
{
public:
	//純虛函式
	//類中只要有一個純虛函式就稱為抽象類
	//抽象類無法實體化物件
	//子類必須重寫父類中的純虛函式,否則也屬于抽象類
	virtual void func() = 0;
};

class Son :public Base
{
public:
	virtual void func() 
	{
		cout << "func呼叫" << endl;
	};
};

void test01()
{
	Base * base = NULL;
	//base = new Base; // 錯誤,抽象類無法實體化物件
	base = new Son;
	base->func();
	delete base;//記得銷毀
}

int main() {

	test01();

	system("pause");

	return 0;
}

4.7.3 虛析構和純虛析構

多型使用時,如果子類中有屬性開辟到堆區,那么父類指標在釋放時無法呼叫到子類的析構代碼

解決方式:將父類中的解構式改為虛析構或者純虛析構

虛析構和純虛析構共性:

  • 可以解決父類指標釋放子類物件
  • 都需要有具體的函式實作

虛析構和純虛析構區別:

  • 如果是純虛析構,該類屬于抽象類,無法實體化物件

虛析構語法:

virtual ~類名(){}

純虛析構語法:

virtual ~類名() = 0;

類名::~類名(){}

? 1. 虛析構或純虛析構就是用來解決通過父類指標釋放子類物件
? 2. 如果子類中沒有堆區資料,可以不寫為虛析構或純虛析構
? 3. 擁有純虛解構式的類也屬于抽象類

class Animal {
public:

	Animal()
	{
		cout << "Animal 建構式呼叫!" << endl;
	}
	virtual void Speak() = 0;

	//解構式加上virtual關鍵字,變成虛解構式
	//virtual ~Animal()
	//{
	//	cout << "Animal虛解構式呼叫!" << endl;
	//}


	virtual ~Animal() = 0;
};

Animal::~Animal()
{
	cout << "Animal 純虛解構式呼叫!" << endl;
}

//和包含普通純虛函式的類一樣,包含了純虛解構式的類也是一個抽象類,不能夠被實體化,

class Cat : public Animal {
public:
	Cat(string name)
	{
		cout << "Cat建構式呼叫!" << endl;
		m_Name = new string(name);
	}
	virtual void Speak()
	{
		cout << *m_Name <<  "小貓在說話!" << endl;
	}
	~Cat()
	{
		cout << "Cat解構式呼叫!" << 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();

	//通過父類指標去釋放,會導致子類物件可能清理不干凈,造成記憶體泄漏
	//怎么解決?給基類增加一個虛解構式
	//虛解構式就是用來解決通過父類指標釋放子類物件
	delete animal;
}

int main() {

	test01();

	system("pause");

	return 0;
}

5 檔案操作

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

通過檔案可以將資料持久化

C++中對檔案操作需要包含頭檔案 < fstream >

檔案型別分為兩種:

  1. 文本檔案 - 檔案以文本的ASCII碼形式存盤在計算機中
  2. 二進制檔案 - 檔案以文本的二進制形式存盤在計算機中,用戶一般不能直接讀懂它們

操作檔案的三大類:

  1. ofstream:寫操作
  2. ifstream: 讀操作
  3. fstream : 讀寫操作

5.1文本檔案

5.1.1寫檔案

寫檔案步驟如下:

  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

#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;
}

總結:

  • 檔案操作必須包含頭檔案 fstream
  • 讀檔案可以利用 ofstream ,或者fstream類
  • 打開檔案時候需要指定操作檔案的路徑,以及打開方式
  • 利用<<可以向檔案中寫資料
  • 操作完畢,要關閉檔案

5.1.2讀檔案

讀檔案與寫檔案步驟相似,但是讀取方式相對于比較多

讀檔案步驟如下:

  1. 包含頭檔案

    #include <fstream>

  2. 創建流物件

    ifstream ifs;

  3. 打開檔案并判斷檔案是否打開成功

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

  4. 讀資料

    四種方式讀取

  5. 關閉檔案

    ifs.close();

#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;
}

總結:

  • 讀檔案可以利用 ifstream ,或者fstream類
  • 利用is_open函式可以判斷檔案是否打開成功
  • close 關閉檔案

5.2 二進制檔案

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

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

5.2.1 寫檔案

二進制方式寫檔案主要利用流物件呼叫成員函式write

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

字符指標buffer指向記憶體中一段存盤空間,len是讀寫的位元組數

#include <fstream>
#include <string>

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

//二進制檔案  寫檔案
void test01()
{
	//1、包含頭檔案

	//2、創建輸出流物件
	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、關閉檔案
	ofs.close();
}

int main() {

	test01();

	system("pause");

	return 0;
}

5.2.2 讀檔案

二進制方式讀檔案主要利用流物件呼叫成員函式read

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

字符指標buffer指向記憶體中一段存盤空間,len是讀寫的位元組數

#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;
}

以上內容,根據網上資料整理匯總,今天我們就到這里,明天繼續努力!

在這里插入圖片描述
若本篇內容對您有所幫助,請三連點贊,關注,收藏支持下,

創作不易,白嫖不好,各位的支持和認可,就是我創作的最大動力,我們下篇文章見!

Dragon少年 | 文

如果本篇博客有任何錯誤,請批評指教,不勝感激 !

轉載請註明出處,本文鏈接:https://www.uj5u.com/ruanti/290328.html

標籤:其他

上一篇:?? 「 演算法 」迭代、比較、移動 ??(推薦收藏)

下一篇:六大排序演算法:插入排序、希爾排序、選擇排序、冒泡排序、堆排序、快速排序

標籤雲
其他(157675) Python(38076) JavaScript(25376) Java(17977) C(15215) 區塊鏈(8255) C#(7972) AI(7469) 爪哇(7425) MySQL(7132) html(6777) 基礎類(6313) sql(6102) 熊猫(6058) PHP(5869) 数组(5741) R(5409) Linux(5327) 反应(5209) 腳本語言(PerlPython)(5129) 非技術區(4971) Android(4554) 数据框(4311) css(4259) 节点.js(4032) C語言(3288) json(3245) 列表(3129) 扑(3119) C++語言(3117) 安卓(2998) 打字稿(2995) VBA(2789) Java相關(2746) 疑難問題(2699) 细绳(2522) 單片機工控(2479) iOS(2429) ASP.NET(2402) MongoDB(2323) 麻木的(2285) 正则表达式(2254) 字典(2211) 循环(2198) 迅速(2185) 擅长(2169) 镖(2155) 功能(1967) .NET技术(1958) Web開發(1951) python-3.x(1918) HtmlCss(1915) 弹簧靴(1913) C++(1909) xml(1889) PostgreSQL(1872) .NETCore(1853) 谷歌表格(1846) Unity3D(1843) for循环(1842)

熱門瀏覽
  • 面試突擊第一季,第二季,第三季

    第一季必考 https://www.bilibili.com/video/BV1FE411y79Y?from=search&seid=15921726601957489746 第二季分布式 https://www.bilibili.com/video/BV13f4y127ee/?spm_id_fro ......

    uj5u.com 2020-09-10 05:35:24 more
  • 第三單元作業總結

    1.前言 這應該是本學期最后一次寫作業總結了吧。總體來說,對作業的節奏也差不多掌握了,作業做起來的效率也更高了。雖然和之前的作業一樣,作業中都要用到新的知識,但是相比之前,更加懂得了如何利用工具以及資料。雖然之間卡過殼,但總體而言,這幾次作業還算完成的比較好。 2.作業程序總結 相比前兩個單元,此單 ......

    uj5u.com 2020-09-10 05:35:41 more
  • 北航OO(2020)第四單元博客作業暨課程總結博客

    北航OO(2020)第四單元博客作業暨課程總結博客 本單元作業的架構設計 在本單元中,由于UML圖具有比較清晰的樹形結構,因此我對其中需要進行查詢操作的元素進行了包裝,在樹的父節點中存盤所有孩子的參考。考慮到性能問題,我采用了快取機制,一次查詢后盡可能快取已經遍歷過的資訊,以減少遍歷次數。 本單元我 ......

    uj5u.com 2020-09-10 05:35:48 more
  • BUAA_OO_第四單元

    一、UML決議器設計 ? 先看下題目:第四單元實作一個基于JDK 8帶有效性檢查的UML(Unified Modeling Language)類圖,順序圖,狀態圖分析器 MyUmlInteraction,實際上我們要建立一個有向圖模型,UML中的物件(元素)可能與同級元素連接,也可與低級元素相連形成 ......

    uj5u.com 2020-09-10 05:35:54 more
  • 6.1邏輯運算子

    邏輯運算子 1. && 短路與 運算式1 && 運算式2 01.運算式1為true并且運算式2也為true 整體回傳為true 02.運算式1為false,將不會執行運算式2 整體回傳為false 03.只要有一個運算式為false 整體回傳為false 2. || 短路或 運算式1 || 運算式2 ......

    uj5u.com 2020-09-10 05:35:56 more
  • BUAAOO 第四單元 & 課程總結

    1. 第四單元:StarUml檔案決議 本單元采用了圖模型決議UML。 UML檔案可以抽象為圖、子圖、邊的邏輯結構。 在實作中,圖的節點包括類、介面、屬性,子圖包括狀態圖、順序圖等。 采用了三次遍歷UML元素的方法建圖,第一遍遍歷建點,第二、三次遍歷設定屬性、連邊,實作圖物件的初始化。這里借鑒了一些 ......

    uj5u.com 2020-09-10 05:36:06 more
  • 談談我對C# 多型的理解

    面向物件三要素:封裝、繼承、多型。 封裝和繼承,這兩個比較好理解,但要理解多型的話,可就稍微有點難度了。今天,我們就來講講多型的理解。 我們應該經常會看到面試題目:請談談對多型的理解。 其實呢,多型非常簡單,就一句話:呼叫同一種方法產生了不同的結果。 具體實作方式有三種。 一、多載 多載很簡單。 p ......

    uj5u.com 2020-09-10 05:36:09 more
  • Python 資料驅動工具:DDT

    背景 python 的unittest 沒有自帶資料驅動功能。 所以如果使用unittest,同時又想使用資料驅動,那么就可以使用DDT來完成。 DDT是 “Data-Driven Tests”的縮寫。 資料:http://ddt.readthedocs.io/en/latest/ 使用方法 dd. ......

    uj5u.com 2020-09-10 05:36:13 more
  • Python里面的xlrd模塊詳解

    那我就一下面積個問題對xlrd模塊進行學習一下: 1.什么是xlrd模塊? 2.為什么使用xlrd模塊? 3.怎樣使用xlrd模塊? 1.什么是xlrd模塊? ?python操作excel主要用到xlrd和xlwt這兩個庫,即xlrd是讀excel,xlwt是寫excel的庫。 今天就先來說一下xl ......

    uj5u.com 2020-09-10 05:36:28 more
  • 當我們創建HashMap時,底層到底做了什么?

    jdk1.7中的底層實作程序(底層基于陣列+鏈表) 在我們new HashMap()時,底層創建了默認長度為16的一維陣列Entry[ ] table。當我們呼叫map.put(key1,value1)方法向HashMap里添加資料的時候: 首先,呼叫key1所在類的hashCode()計算key1 ......

    uj5u.com 2020-09-10 05:36:38 more
最新发布
  • 【中介者設計模式詳解】C/Java/JS/Go/Python/TS不同語言實作

    * 中介者模式是一種行為型設計模式,它可以用來減少類之間的直接依賴關系,
    * 將物件之間的通信封裝到一個中介者物件中,從而使得各個物件之間的關系更加松散。
    * 在中介者模式中,物件之間不再直接相互互動,而是通過中介者來中轉訊息。 ......

    uj5u.com 2023-04-20 08:20:47 more
  • 露天煤礦現場調研和交流案例分享

    他們集團的資訊化公司及研究院在一個礦區正在做智能礦山的統一平臺的 試點,專案投資大概1億,包括了礦山的各方面的內容,顯示得我們這次交流有點多余。他們2年前開始做智能礦山的規劃,有很多煤礦行業專家的加持,他們的描述是非常完美,但是去年底應該上線的平臺,現在還沒有看到影子。他們確實有很多場景需求,但是被... ......

    uj5u.com 2023-04-20 08:20:25 more
  • 《社區人員管理》實戰案例設計&個人案例分享

    設計是一個讓人夢想成真程序,開始編碼、測驗、除錯之前進行需求分析和架構設計,才能保證關鍵方面都做正確 ......

    uj5u.com 2023-04-20 08:20:17 more
  • 軟體架構生態化-多角色交付的探索實踐

    作為一個技術架構師,不僅僅要緊跟行業技術趨勢,還要結合研發團隊現狀及痛點,探索新的交付方案。在日常中,你是否遇到如下問題 “ 業務需求排期長研發是瓶頸;非研發角色感受不到研發技改提效的變化;引入ISV 團隊又擔心質量和安全,培訓周期長“等等,基于此我們探索了一種新的技術體系及交付方案來解決如上問題。 ......

    uj5u.com 2023-04-20 08:20:10 more
  • 【中介者設計模式詳解】C/Java/JS/Go/Python/TS不同語言實作

    * 中介者模式是一種行為型設計模式,它可以用來減少類之間的直接依賴關系,
    * 將物件之間的通信封裝到一個中介者物件中,從而使得各個物件之間的關系更加松散。
    * 在中介者模式中,物件之間不再直接相互互動,而是通過中介者來中轉訊息。 ......

    uj5u.com 2023-04-20 08:19:44 more
  • 露天煤礦現場調研和交流案例分享

    他們集團的資訊化公司及研究院在一個礦區正在做智能礦山的統一平臺的 試點,專案投資大概1億,包括了礦山的各方面的內容,顯示得我們這次交流有點多余。他們2年前開始做智能礦山的規劃,有很多煤礦行業專家的加持,他們的描述是非常完美,但是去年底應該上線的平臺,現在還沒有看到影子。他們確實有很多場景需求,但是被... ......

    uj5u.com 2023-04-20 08:19:07 more
  • 《社區人員管理》實戰案例設計&個人案例分享

    設計是一個讓人夢想成真程序,開始編碼、測驗、除錯之前進行需求分析和架構設計,才能保證關鍵方面都做正確 ......

    uj5u.com 2023-04-20 08:18:57 more
  • 軟體架構生態化-多角色交付的探索實踐

    作為一個技術架構師,不僅僅要緊跟行業技術趨勢,還要結合研發團隊現狀及痛點,探索新的交付方案。在日常中,你是否遇到如下問題 “ 業務需求排期長研發是瓶頸;非研發角色感受不到研發技改提效的變化;引入ISV 團隊又擔心質量和安全,培訓周期長“等等,基于此我們探索了一種新的技術體系及交付方案來解決如上問題。 ......

    uj5u.com 2023-04-20 08:18:49 more
  • 05單件模式

    #經典的單件模式 public class Singleton { private static Singleton uniqueInstance; //一個靜態變數持有Singleton類的唯一實體。 // 其他有用的實體變數寫在這里 //構造器宣告為私有,只有Singleton可以實體化這個類! ......

    uj5u.com 2023-04-19 08:42:51 more
  • 【架構與設計】常見微服務分層架構的區別和落地實踐

    軟體工程的方方面面都遵循一個最基本的道理:沒有銀彈,架構分層模型更是如此,每一種都有各自優缺點,所以請根據不同的業務場景,并遵循簡單、可演進這兩個重要的架構原則選擇合適的架構分層模型即可。 ......

    uj5u.com 2023-04-19 08:42:41 more