c++學習筆記——模板和IO(一)

来源:https://www.cnblogs.com/cloudseastore/archive/2023/02/19/17135769.html
-Advertisement-
Play Games

前言 本文主要作為本人學習C\C++歷程的的一種記錄,以期望通過這種方式加深對知識點的記憶,查漏補缺。如有寫得不對的地方,歡迎大家批評改正。 模板概論 模板是泛型編程的基礎,是創建泛型類或函數的藍圖或公式。 C++提供了兩種模板機制:函數模板和類模板。函數模板,實際上是建立了一個通用函數,其函數類型 ...


前言

本文主要作為本人學習C\C++歷程的的一種記錄,以期望通過這種方式加深對知識點的記憶,查漏補缺。如有寫得不對的地方,歡迎大家批評改正。

模板概論

模板是泛型編程的基礎,是創建泛型類或函數的藍圖或公式。
C++提供了兩種模板機制:函數模板和類模板。函數模板,實際上是建立了一個通用函數,其函數類型和形參類型不具體制定,用一個虛擬的類型來代表。類模板和函數模板的定義和使用類似。
下麵開始逐一介紹C++模板的應用。

函數模板

定義函數模板

點擊查看代碼
template<class T>	//註意:T代表泛型的數據類型,不是只能寫T,
void mySwap(T& a, T& b)
{
	T temp = a;
	a = b;
	b = temp;
}

也可以這樣定義函數模板

點擊查看代碼
template<typename T>
void mySwap(T& a,T& b)
{

}

怎麼使用函數模板?

點擊查看代碼
template<class T>	//註意:T代表泛型的數據類型,不是只能寫T,
void mySwap(T& a, T& b)
{
	T temp = a;
	a = b;
	b = temp;
}
void test01()
{
	int a = 10;
	int b = 20;
	mySwap(a, b);
	cout << "a = " << a << endl;
	cout << "b = " << b << endl;
}

//可以這樣定義函數模板
//template<typename T>
//void func(T a, T b)
//{
//
//}

template<class T>
T func1(T a, T b)
{
	return a + b;
}
void test02()
{
	int a = 10;
	double b = 20.3;
	//如果使用參數列表指定數據類型,那麼實參中可以隱式轉換,
	//如果轉換成功,就調用,轉換不成功就報錯
	cout << func1<int>(a, b) << endl;
}

普通函數和函數模板的區別

點擊查看代碼
//1.普通函數可以進行隱式轉換,函數模版不能直接的進行隱性轉換

//普通函數
int myPlus(int a, int b)
{
	cout << "普通函數" << endl;
	return a + b;
}

//函數模板
template<class T>
int myPlus(T a, T b)
{
	cout << "函數模板" << endl;
	return a + b;
}

void test01()
{
	int a = 10;
	double b = 20;
	myPlus(a, b);	//普通函數可以進行隱性轉換
	//myPlus2(a, b);	//沒有與參數列表匹配的 函數模板 "myPlus2" 實例	
	myPlus<int>(a, b);	//如果要進行隱性轉換,必須加上參數列表
}

普通函數和函數模板的調用規則

點擊查看代碼
//普通函數
int myPlus(int a, int b)
{
	cout << "普通函數" << endl;
	return a + b;
}

//函數模板
template<class T>
int myPlus(T a, T b)
{
	cout << "函數模板" << endl;
	return a + b;
}
//函數模板重載
template<class T>
void myPlus(T a, T b, T c)
{
	cout << "函數模板myPlus(T a, T b, T c)" << endl;
}
//1、函數模板和普通函數都可以重載
void test02()
{
	int a = 10;
	int b = 20;
	//2、如果普通函數和函數模板都可以實現的功能,普通函數優先調用
	myPlus(a, b);
	
	//3、可以使用<>空參數列表強制調用函數模板
	myPlus<>(a, b);

	//4、函數模板之間也可以進行重載

	//5、如果函數模板可以產生更好的匹配,那麼優先使用函數模板
	char c1 = 'a';
	char c2 = 'b';
	myPlus(c1, c2);
}

模板機制剖析——c++編譯器是如何實現函數模板機制的?

  1. 編譯器並不是把函數模板處理成能夠處理任何類型的函數
  2. 函數模板通過具體類型產生不同的函數
  3. 編譯器會對函數模板進行兩次編譯,在聲明的地方對模板代碼本身進行編譯,在調用的地方對參數替換後的代碼進行編譯。

模板也具有局限性

點擊查看代碼
template<class T>
void mycompare(T a, T b)
{
	if (a > b)
	{
		cout << "a>b" << endl;
	}
	else
	{
		cout << "a<=b" << endl;
	}
}

void test01()
{
	//如果傳入的是數組名,那麼函數模板中比較函數名的大小就沒有意義
	int arr[20];
	int arr2[10];

	mycompare(arr, arr2);
}

為解決上述問題,提出了兩種方法

第一種:使用函數模板具體化

點擊查看代碼
class Maker
{
public:
	Maker(string name, int age)
	{
		this->name = name;
		this->age = age;
	}
public:
	string name;
	int age;
};

template<class T>
void myfunc(T& a, T& b)
{
	if (a > b)
	{
		cout << "a>b" << endl;
	}
	else
	{
		cout << "a<=b" << endl;
	}
}
//具體化函數模板,註意上面的函數模板要有,才能具體化
template<>void myfunc<Maker>(Maker& a, Maker& b)
{
	cout << "函數模板的具體化" << endl;
	if (a.age > b.age)
	{
		cout << "a>b" << endl;
	}
	else
	{
		cout << "a<=b" << endl;
	}
}

第二種:重載操作符

點擊查看代碼
class Maker1
{
public:
	Maker1(string name, int age)
	{
		this->name = name;
		this->age = age;
	}
public:
	string name;
	int age;
};

bool operator>(Maker1& m1, Maker1& m2)
{
	if (m1.name > m2.name && m1.age > m2.age)
	{
		return true;
	}
	else
	{
		return false;
	}
}

void test()
{
	Maker1 a("aaa", 18);
	Maker1 b("bbb", 20);
	if (a > b)
	{
		cout << "a>b" << endl;
	}
	else
	{
		cout << "a<=b" << endl;
	}
}

定義類模板

點擊查看代碼
//類模板是把類中的數據類型參數化
template<class NameType,class AgeType>
class Maker
{
public:
	Maker(NameType name, AgeType age)
	{
		this->name = name;
		this->age = age;
	}
	void printMaker()
	{
		cout << "Name:" << this->name << "Age:" << this->age << endl;
	}
public:
	NameType name;
	AgeType age;
};

類模板的使用

點擊查看代碼
template<class NameType,class AgeType>
class Maker
{
public:
	Maker(NameType name, AgeType age)
	{
		this->name = name;
		this->age = age;
	}
	void printMaker()
	{
		cout << "Name:" << this->name << "Age:" << this->age << endl;
	}
public:
	NameType name;
	AgeType age;
};

//類模板的使用
void test()
{
	//1.類模版不會自動推導數據類型,要顯示的告訴編譯器是什麼類型
	Maker<string, int> m("高啟強", 35);
	m.printMaker();

	//2.註意傳入的參數,傳入參數類型要程式員自己把握
	Maker<int, int>m2(18, 20);
	m2.printMaker();

	//Maker<>m3("aaa", 18);	//err:類模板“Maker"的參數太少,必須通過參數列表告訴編譯器是什麼類型

}

類模板和函數模板的區別

  1. 類模板不會自動推導數據類型,要顯示的告訴編譯器是什麼類型
  2. 函數模板可以根據實參來推導數據類型

類模板的預設類型及其使用

點擊查看代碼
template<class NameType, class AgeType=int>
class Maker2
{
public:
	Maker2(NameType name, AgeType age)
	{
		this->name = name;
		this->age = age;
	}

	void printMaker()
	{
		cout << "Name:" << this->name << "Age:" << this->age << endl;
	}
public:
	NameType name;
	AgeType age;
};

void test02()
{
	//如果有預設類型,那麼<>里可以少寫類型
	Maker2<string> m("高啟強", 35);
	//Maker2<string, int> m("高啟蘭", 20);
	m.printMaker();
	//以傳入的類型為準
	Maker2<string, double> m2("高啟盛", 24.22);
	m2.printMaker();
}

//5、類模板的預設參數註意
//預設類型後面的泛型類型都必須有預設類型
template<class NameType, class AgeType = int, class T = int>
class Maker3
{
public:
	Maker3(NameType name, AgeType age)
	{
		this->name = name;
		this->age = age;
	}

	void printMaker()
	{
		cout << "Name:" << this->name << " Age:" << this->age << endl;
	}
public:
	NameType name;
	AgeType age;
};

void test03()
{
	Maker3<string> m("高啟強", 35);
	m.printMaker();
}

類模板做函數參數

點擊查看代碼
template<class NameType, class AgeType>
class Maker
{
public:
	Maker(NameType name, AgeType age)
	{
		this->name = name;
		this->age = age;
	}
	void printMaker()
	{
		cout << "name = " << this->name << " " << " age = " << this->age << endl;
	}
public:
	NameType name;
	AgeType age;
};

//類模板做函數參數
	//1.指定傳入的數據類型
void func(Maker<string, int>& m)
{
	m.printMaker();
}


	//2.參數模版化(常用)
template<class T1, class T2>
void func2(Maker<T1, T2>& m)
{
	m.printMaker();
}

	//3.整個類 模版化
template<class T>
void func3(T& m)
{
	m.printMaker();
}

類模板的繼承

點擊查看代碼
//1、普通類繼承類模板
template<class T>
class Father
{
public:
	Father()
	{
		m = 20;
	}
public:
	T m;
};

//普通類 繼承 類模板
class Son : public Father<int>	//2、要告訴編譯器父類的泛型數據類型具體是什麼類型
{
public:

};

//2、類模板 繼承 類模板
//類模板 繼承 類模板
template<class T1,class T2>
class Son2 :public Father<T2>	//要告訴編譯器父類的泛型數據類型具體是什麼類型
{

};

類模板的成員函數的類內實現

點擊查看代碼
template<class NameType,class AgeType>
class Maker
{
public:
	Maker(NameType name, AgeType age)
	{
		this->name = name;
		this->age = age;
	}

	void printMaker()
	{
		cout << "Name:" << this->name << " " << "Age:" << this->age << endl;
	}
public:
	NameType name;
	AgeType age;
};

類模板的成員函數類外實現

點擊查看代碼
template<class NameType,class AgeType>
class Maker
{
public:
	Maker(NameType name, AgeType age);

	void printMaker();
public:
	NameType name;
	AgeType age;
};

//類模板的成員函數類外實現
//成員函數必須寫成函數模板,並且寫上參數列表
template<class NameType,class AgeType>
Maker<NameType,AgeType>::Maker(NameType name, AgeType age)
{
	cout << "構造函數" << endl;
	this->name = name;
	this->age = age;
}

template<class NameType,class AgeType>
void Maker<NameType, AgeType>::printMaker()
{
	cout << "Name:" << this->name << " " << "Age:" << this->age << endl;
}

類模板的友元實現

類內實現

點擊查看代碼
template<class NameType,class AgeType>
class Maker
{
	friend void printMaker(Maker<NameType, AgeType>& p)
	{
		cout << "類內實現" <<" "<< p.name << " " << p.age << endl;
	}
public:
	Maker(NameType name, AgeType age)
	{
		this->name = name;
		this->age = age;
	}
private:
	NameType name;
	AgeType age;

};

類外實現

點擊查看代碼
//1、聲明模板
template<class NameType,class AgeType>
class Maker2;
//2、聲明函數模板
//告訴編譯器下麵有printMaker2的實現
template<class NameType,class AgeType>
void printMaker2(Maker2<NameType, AgeType>& p);

template<class NameType, class AgeType>
class Maker2
{
	//3、在函數名和()之間加上<>
	friend void printMaker2<>(Maker2<NameType, AgeType>& p);
	//編譯器不知道printMaker2下麵有沒有實現,需要知道函數的結構
public:
	Maker2(NameType name, AgeType age)
	{
		this->name = name;
		this->age = age;
	}
private:
	NameType name;
	AgeType age;
};
//友元在類外實現要寫成函數模板
template<class NameType,class AgeType>
void printMaker2(Maker2<NameType, AgeType>& p)
{
	cout << "類外實現的友元函數" << " "<<p.name << " " << p.age << endl;
}

類模板實現數組

MyArray.hpp

點擊查看代碼
#pragma once

template<class T>
class MyArray
{
public:
	MyArray(int capacity)
	{
		this->mCapacity = capacity;
		this->mSize = 0;
		//T如果是Maker,這裡要調用什麼構造函數,要調用無參構造
		p = new T[this->mCapacity];
	}
	//拷貝構造
	MyArray(const MyArray &arr)
	{
		this->mCapacity = arr.mCapacity;
		this->mSize = arr.mSize;
		p = new T[arr.mCapacity];

		for (int i = 0; i < this->mSize; i++)
		{
			p[i] = arr.p[i];
		}
	}
	//賦值函數
	MyArray& operator=(const MyArray& arr)
	{
		if (this->p != NULL)
		{
			delete[] this->p;
			this->p = NULL;
		}
		p = new T[arr.mCapacity];
		this->mSize = arr.mSize;
		this->mCapacity = arr.mCapacity;
		for (int i = 0; i < this->mSize; i++)
		{
			p[i] = arr.p[i];
		}

		return *this;
	}
	
	//重載[]
	T& operator[](int index)
	{
		return this->p[index];
	}
	//尾插
	void Push_Back(const T& val)
	{
		if (this->mSize == this->mCapacity)
		{
			return;
		}
		this->p[this->mSize] = val;
		this->mSize++;
	}
	//尾刪
	void Pop_Back()
	{
		if (this->mSize == 0)
		{
			return;
		}
		this->mSize--;
	}
	//析構
	~MyArray()
	{
		if (this->p != NULL)
		{
			delete[] p;
			p = NULL;
		}
		cout << "析構函數" << endl;
	}

	int getSize()
	{
		return this->mSize;
	}

private:
	T* p;
	int mCapacity;
	int mSize;
};
類模板實現數組.cpp
點擊查看代碼
#define _CRT_SECURE_NO_WARNINGS
#include<string>
#include<iostream>
using namespace std;
#include"MyArray.hpp"
class Maker
{
public:
	Maker() 
	{
		cout << "無參構造" << endl;
	}
	Maker(string name,int age)
	{
		this->name = name;
		this->age = age;
	}
public:
	string name;
	int age;
};

void printMaker(MyArray<Maker>& arr)
{
	for (int i = 0; i < arr.getSize(); i++)
	{
		cout << "姓名:" << arr[i].name << " 年齡:" << arr[i].age << endl;
	}
}

void test()
{
	//Maker類型
	MyArray<Maker>myarr(4);
	Maker m1("小明", 18);
	Maker m2("小強", 19);
	Maker m3("小棟", 20);
	Maker m4("小興", 21);
	myarr.Push_Back(m1);
	myarr.Push_Back(m2);
	myarr.Push_Back(m3);
	myarr.Push_Back(m4);
	printMaker(myarr);

	//int類型
	MyArray<int>myint(10);
	for (int i = 0; i < 10; i++)
	{
		myint.Push_Back(i + 1);
	}
	for (int i = 0; i < 10; i++)
	{
		cout << myint[i] << " " << endl;
	}
}

int main()
{
	test();
	system("pause");
	return EXIT_SUCCESS;
}

總結

模板是一個比較重要的概念,是創建泛型類或函數的藍圖或公式。庫容器,比如迭代器和演算法,都是泛型編程的例子,它們都使用了模板的概念。
-學習C++任重而道遠,本人愚鈍,唯有勤加練習方能查漏補缺。


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 項目背景 最近在準備開發工作流引擎相關模塊,完成表結構設計後開始著手流程設計器的技術選型,調研了眾多開源項目後決定基於jsplumb.js開源庫進行自研開發,保證定製化的便捷性,相關效果圖及項目地址如下 項目地址:https://gitee.com/code2roc/fast-flow-desgio ...
  • ES6的模塊化設計思想是靜態化,也就是說,在編譯的時候確定模塊的依賴關係,以及輸出輸出入的變數。而CommonJS和AMD模塊都是在運行時確定的。ES6的模塊不是對象,而是通過export顯示指定輸出的代碼,再通過import命令輸入。 // 模塊輸入 import { start,address ...
  • Cesium中的entities可以隨時間變化長度高度,主要依賴於CallbackProperty函數。 因為使用CallbackProperty,cesium中一切可視化的要素都可以與時間聯繫起來。 定義:new Cesium.CallbackProperty (callback, isConst ...
  • 實踐環境 Odoo 14.0-20221212 (Community Edition) 需求描述 如下圖,列表網倉記錄詳情頁面(form視圖),編輯內聯視圖中的貨主記錄,為貨主和倉庫欄位搜索,添加過濾條件,具體如下: 添加、編輯貨主時,下拉列表中只展示選取和當網倉記錄所屬公司關聯的貨主,點擊搜索更多 ...
  • 實踐環境 Odoo 14.0-20221212 (Community Edition) 代碼實現 在js腳本函數中調用模型中自定義方法: this._rpc({ model: 'demo.wizard', // 模型名稱,即模型類定義中 _name 的值 method: 'action_select ...
  • 題目來源 45. 跳躍游戲 II 題目詳情 給定一個長度為 n 的 0 索引整數數組 nums。初始位置為 nums[0]。 每個元素 nums[i] 表示從索引 i 向前跳轉的最大長度。換句話說,如果你在 nums[i] 處,你可以跳轉到任意 nums[i + j] 處: 0 <= j <= nu ...
  • 實踐環境 Odoo 14.0-20221212 (Community Edition) 需求描述 如圖,給表單新增一個類似聊天的視窗,當記錄一些表單活動(本例為自動記錄當前記錄狀態變化) 需求實現 模型定義 odoo14\custom\estate\models\estate_property.py ...
  • 異常處理 1.基本介紹 SpringMVC 通過 HandlerExceptionResolver 處理程式的異常,包括 Handler映射、數據綁定以及目標方法執行時發生的異常 有兩種方案來進行異常處理: a.在本類編寫處理異常的方法,將拋出的異常視為局部異常處理 b.額外編寫處理異常的類,將拋出 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...