0
点赞
收藏
分享

微信扫一扫

4月21日作业

知年_7740 2023-04-26 阅读 108

1、函数模板

模板就是建立通用的模具,提高程序复用性。C++提供两种模板机制:函数模板和类模板

1.1 函数模板语法

作用:建立一个通用函数,其函数返回值类型和形参类型可以不具体制定,用一个虚拟的类型来代表。
模板是为了提高复用性,将数据类型参数化。

  • template template --生命创建模板, --typename 表明后面的符号一种数据类型 --T 通用的数据类型,名称可以替换,通常为大写字母
  • 函数声明或定义
template<typename T> //声明一个模板,告诉编译器后面代码中紧跟着的T不要报错,T是一个通用数据类型
void mySwap(T &a, T &b) {
	T t = a;
	a = b;
	b = t;
}
int main() {
	int a = 2;
	int b = 3;
	float c = 1.3;
	float d = 2.1;
	// 两种方式使用函数模板
	// 自动类型推导;显示指定
	mySwap(a, b);
	mySwap<float>(c, d);
	cout << c << d;
	return 1;
}

1.2 函数模板注意事项

  • 自动类型推导,必须推导出一致的数据类型T,才可以使用
  • 模板必须要确定出T的数据类型,才可以使用
template<class T>
void func() {
	cout << "hello";
}
int main() {
	func();  //错误,没有指定函数模板类型
	func<int>();
	return 1;
}

利用函数模板对数组进行排序

template<class T>
void myswap(T& a, T& b) {
	T t;
	t = a;
	a = b;
	b = t;
}
template<typename T> //typename可以替换为class,没有任何区别
void mySort(T arr[], int len) {  //数组,长度
	int max;
	for (int i = 0; i < len - 1; i++) {
		max = i;
		for (int j = i+1; j < len; j++) {
			if (arr[max] < arr[j]) {
				max = j;
			}
		}
		if (max != i) {
			myswap(arr[i], arr[max]);
		}
		
	}
}

template<class T>
void printArr(T arr[],int len) {
	for (int i = 0; i < len; i++) {
		cout << arr[i]<<" ";
	}
}
int main() {
	int arr[4] = {3,1,5,2};
	char arr_char[] = "eqfa";
	mySort(arr_char, sizeof(arr_char)/sizeof(char)); //数组名,数组长度
	mySort(arr, sizeof(arr_char) / sizeof(char));
	printArr(arr,4);
	return 1;
}

1.3 普通函数与函数模板的区别

  • 普通函数调用时可以发生自动类型转换(隐式类型转换)
  • 函数模板调用时,如果利用自动类型推导,不会发生隐式类型转换
  • 如果利用显示指定类型的方式,可以发生隐式类型转换
//普通函数会发生隐式类型转换
int myAdd(int a, int b) {
	return a + b;
}
//函数模板不会主动隐式类型转换
template<class T>
T myAdd01(T a,T b) {
	return a + b;
}
int main() {
	cout << myAdd01<int>('a', 'c'); // 不加<int> 不会将字符型转换为整型
	cout << myAdd01(2, 4);
	return 1;
}

1.4 普通函数与函数模板调用的规则

  • 1、如果函数模板和普通函数都可以实现,优先调用普通函数
  • 2、可以通过空模板参数列表的方式强制调用函数模板
  • 3、函数模板也可以重载
  • 4、如果函数模板可以产生更好的匹配,优先调用函数模板
void myPrint(int a, int b) { 
	cout << "调用的普通函数" << endl;
}

template<class T> //函数模板的重载
void myPrint(T a, T b, T c) {
	cout << "调用的模板" << endl;
}

int main() {
	myPrint(2, 3);  //调用普通函数
	//myPrint<>(2, 3);
	myPrint(5, 3, 1);  //通过空模板参数列表调用普通函数
	char c1 = 'a';
	char c2 = 'b';
	myPrint(c1, c2, 'x'); // 根据类型推断,优先调用函数模板
	return 1;
}

1.5 模板的局限性

模板的通用性并不是万能的,有些特定数据类型,需要用具体化方式做特殊实现。

void myPrint(int a, int b) { 
	cout << "调用的普通函数" << endl;
}
class Person {
public:
	int age;
	string name;
	Person(int age,string name) {
		this->age = age;
		this->name = name;
	}
};
template<class T>
bool myCompare(T &a,T &b) {
	if (a == b) {
		return true;
	}
	else {
		return false;
	}
}
//利用具体化Person的版本实现代码,具体化优先调用
template<>bool myCompare(Person& p1, Person& p2)
{
	if (p1.name == p2.name && p1.age == p2.age) {
		return true;
	}
	else {
		return false;
	}
}

int main() {
	Person p1(43,"曹操");
	Person p2(32,"曹植");
	cout<<myCompare(p1,p2);
	return 1;
}

1.6 类模板

作用:建立一个通用类,类中的成员 数据类型可以不具体指定,用一个虚拟的类型来代表

  • template
template<class NameType,class AgeType>
class Person {
public:
	NameType name;
	AgeType age;
	Person(NameType name,AgeType age) {
		this->age = age;
		this->name = name;
	}

};
int main() {
	Person<string,int> p1("曹操",32); //分别给类型传值和构造函数传值
	cout << p1.name<<" "<<p1.age;
	return 1;
}

1.7 类模板和函数模板的区别

  • 1、类模板没有自动类型推导的使用方式
  • 2、类模板在模板参数列表中可以有默认参数
template<class NameType,class AgeType=int> //指定默认参数类型
class Person {
public:
	NameType name;
	AgeType age;
	Person(NameType name,AgeType age) {
		this->age = age;
		this->name = name;
	}
	void showPerson() {
		cout << name <<" "<< age;
	}
};
int main() {
	Person<string,int> p1("曹操",32); //分别给类型传值和构造函数传值
	//Person p("曹冲",13); // 无法进行自动类型推导
	Person<string,int> p("曹冲",13); //只能用显示指定类型
	Person<string> p("曹冲",13); //默认参数类型
	return 1;
}

1.8 类模板中成员函数创建时机

类模板中成员函数和普通类中成员函数创建时机是有区别的

  • 普通类中的成员函数一开始就可以创建
  • 类模板中的成员函数在调用时才创建

1.9 类模板对象做函数参数

  • 1、指定传入的类型 —直接显示对象的数据类型
  • 2、参数模板化 —将对象中的参数变为模板进行传递
  • 3、整个类模板化 —将这个对象类型模板化进行传递
template<class T1,class T2>
class Person {
public:
	T1 name;
	T2 age;
	Person(T1 name,T2 age) {
		this->age = age;
		this->name = name;
	}
	void showPerson(){
		cout << name << " " << age;
	}
};
void printPerson1(Person<string,int>&p) { //指定传入类型
	p.showPerson();
}
template<class T1,class T2>
void printPerson2(Person<T1, T2>&p) { //参数模板化
	p.showPerson();
	cout << typeid(T1).name()<<endl;
	cout << typeid(T2).name();
}
template<class T>
void printPerson3(T &p) {//整个类模板化
	p.showPerson();
	cout << typeid(T).name() << end;
}
void test01() {
	Person<string, int>p("曹冲", 13);
	printPerson1(p);
}
void test02() {
	Person<string, int>p("曹冲", 13);
	printPerson2(p);
}
void test03() {
	Person<string, int>p("曹植", 34);
	printPerson3(p);
}

1.10 类模板与继承

  • 当子类继承的父类是一个类模板时,子类在声明的时候,需要指定出父类中T的类型
  • 如果不指定,编译器无法给子类分配内存
  • 如果想灵活指定父类中T的类型,子类也需创建为类模板
template<class T1,class T2>
class Base {

};
template<class T3,class T2,class T1>
class Son :public Base<T1,T2>{
public:
	T3 obj; 
	Son() {
		cout << typeid(T1).name() << endl;
		cout << typeid(T3).name() << endl;
		cout << typeid(T2).name() << endl;
	}
};
int main() {
	Son<char, int, string>s;
	return 1;
}

1.11 类模板成员函数的类外实现

template<class T1,class T2>
class Person {
public:
	T1 name;
	T2 age;
	Person(T1 name,T2 age);
	void showPerson();
	/*person(t1 name,t2 age) {
		this->age = age;
		this->name = name;
	}
	void showperson() {
		cout << age << " " << name;
	}*/
};
template<class T1, class T2>
void Person<T1,T2>::showPerson() {
	cout << age << " " << name;
}
template<class T1,class T2>
Person<T1,T2>::Person(T1 name,T2 age) {
	this->age = age;
	this->name = name;
}

1.12 类模板分文件编写

类模板中成员函数创建时机是在调用阶段,导致分文件编写时链接不到

  • 直接包含cpp源文件
  • 将.h和.cpp内容写在一起,将后缀名改为.hpp文件

1.13 类模板与友元

全局函数类内实现–直接在类内添加友元声明
全局函数类外实现–需要声明模板类

template<class T1,class T2>
class Person;
template<class T1, class T2>
void printPerson1(const Person<T1, T2>& p) {
	cout << p.name << " " << p.age;
}
template<class T1,class T2>
class Person {
	//全局函数类内实现
	friend void printPerson(const Person<T1, T2>& p) {
		cout << p.name << " " << p.age;
	}
	//全局函数类外实现
	friend void printPerson1<>(const Person<T1, T2>& p);
public:
	Person(T1 age,T2 name) {
		this->age = age;
		this->name = name;
	}
private:
	T1 age;
	T2 name;
};

1.14 案例

案例描述:实现一个通用的数组类,需求如下:

  • 可以对内置数据类型以及自定义数据类型的数据进行存储
  • 将数组中的数据传入数组的容量
  • 构造函数中可以传入数组的容量
  • 提供对应的拷贝构造函数以及operator=防止浅拷贝问题
  • 提供尾插法和尾删法对数组中的数据进行增加和删除
  • 可以通过下标的方式访问数组中的元素
  • 可以获取数组中当前元素个数和数组的容量
//Main.cpp
#include <iostream>
#include "MyArray.hpp"
using namespace std;

class Person {
public:
	Person() {};
	Person(string name, int age) {
		this->age = age;
		this->name = name;
	}
	string name;
	int age;
};
void printIntArray(MyArray <int>&arr) {
	for (int i = 0; i < arr.getSize(); i++) {
		cout << arr[i] << endl;
	}
}
void test01() {
	MyArray<int>arr1(5);
	for (int i = 0; i < 5; i++) {
		arr1.push_back(i);
	}
	cout << "array的打印输出为:";
	printIntArray(arr1);
	arr1.pop_back();
	arr1.pop_back();
	cout << arr1.getSize();
	cout << arr1.getCapacity();
	cout << arr1[4];
}
void printPersonArray(MyArray<Person>&arr) {
	for (int i = 0; i < arr.getSize(); i++) {
		cout << arr[i].name << " " << arr[i].age << endl;
	}
	//cout << arr.getCapacity() << endl;
}
void test02() {
	Person p1("曹冲",13);
	Person p2("曹植",23);
	Person p3("曹丕",43);
	MyArray<Person>arr(3);
	arr.push_back(p1);
	arr.push_back(p2);
	arr.push_back(p3);
	printPersonArray(arr);
}
int main() {
	test02();
	return 1;
}
//MyArray.hpp
#pragma once
#include<iostream>
using namespace std;

template<class T>
class MyArray {
public:
	MyArray(int capacity) {		
		this->capacity = capacity;
		this->size = 0;
		this->pAddress = new T[this->capacity];
	}
	MyArray(const MyArray & arr) {
		this->capacity = arr.capacity;
		this->size = arr.size;
		this->pAddress = new T[arr.capacity]; //深拷贝
		for (int i = 0; i < this->size; i++) {
			this->pAddress[i] = arr.pAddress[i];
		}
	}
	MyArray& operator=(const MyArray& arr) {
		//先判断原来堆区是否有数据,如果有先释放
		if (this->pAddress != NULL) {
			delete [] this->pAddress;
			this->pAddress = NULL;
			this->size = 0;
		}
		//深拷贝
		this->capacity = arr.capacity;
		this->size = arr.size;
		this->pAddress = new T[arr, capacity];
		for (int i = 0; i < this->size; i++) {
			this->pAddress[i] = arr.pAddress[i];
		}
		return *this;
	}
	void push_back(const T& t) {
		if (this->capacity == this->size) {
			return;
		}
		else {
			this->pAddress[this->size] = t;
			this->size++;
		}
	}
	void pop_back() {
		if (this->size==0) {
			return;
		}
		else {
			this->size--;
		}
	}
	T& operator[](int index) {
		return this->pAddress[index];
	}
	int getCapacity() {
		return this->capacity;
	}
	int getSize() {
		return this->size;
	}
	~MyArray() {
		cout << "析构函数";
		if (this->pAddress != NULL) {
			delete[] this->pAddress;
			this->pAddress = NULL;
		}		
	}
private:
	T* pAddress; //指针指向堆区开辟的真实数组
	int capacity;
	int size;
};
举报

相关推荐

4月4日java作业

4月25日作业

4月12日作业

4月29日作业

4月23日作业

4月22日作业

4月13日作业

安全作业4月13日

4月25日大作业(半成品)

0 条评论