38 函数模板和类模板

泛型编程

1 函数模板本质:类型参数化

产生模板的原因:业务逻辑一样;函数参数类型不一样

#include<iostream>
using namespace std;


//业务逻辑一样
//函数参数类型不一样
void myswap01(int& a, int& b) {
	int c = a;
	a = b;
	b = c;
}

void myswap01(char& a, char& b) {
	char c = a;
	a = b;
	b = c;
}


//类型参数化 泛型编程
void main() {
	{
		int x = 10;
		int y = 20;
		myswap01(x, y);
		cout << "x:" << x << endl;
		cout << "y:" << y << endl;
	}

	{
		int x = 'a';
		int y = 'b';
		myswap01(x, y);
		cout << "x:" << (char)x << endl;
		cout << "y:" << (char)y << endl;
	}
}

1 函数模板例子

//类型参数化 泛型编程
//template C++泛型编程
template <typename T>
void myswap(T &a, T &b) {
	T c = a;
	a = b;
	b = c;
}

// 函数模板调用
// 显示类型调用
// 自动类型推导
void main() {
	{
		int x = 10;
		int y = 20;

		myswap<int>(x, y);	//函数模板 显示类型调用
		myswap(x, y);		//函数模板 自动类型推导
		cout << "x:" << x << endl;
		cout << "y:" << y << endl;
	}

	{
		char x = 'a';
		char y = 'b';

		myswap<char>(x, y);	//函数模板 显示类型调用
		myswap(x, y);
		cout << "x:" << (char)x << endl;
		cout << "y:" << (char)y << endl;
	}
}

2 函数模板做函数参数

#include<iostream>
using namespace std;

// 对字符数组和int数组排序
template<typename T, typename T2>
int mySort(T* array, T2 size) {
	if (array == NULL) {
		return -1;
	}

	for (T2 i = 0; i < size; i++) {
		for (T2 j = i + 1; j < size; j++) {
			if (array[i] < array[j]) {
				T temp = array[i];
				array[i] = array[j];
				array[j] = temp;
			}
		}
	}

	return 0;
}

template<typename T, typename T2>
int myPrint(T *array, T2 size) {
	for (T2 i = 0; i < size; i++) {
		cout << array[i] << " " ;
	}
	return 0;
}

void main() {
	// int 类型
	{
		int myarray[] = { 11,23,43,1,53,4,9,3 };
		int size = sizeof(myarray) / sizeof(*myarray);
		mySort<int, int>(myarray, size);
		cout << "排序之后......" << endl;
		myPrint(myarray, size);
	}
	// char 类型
	{
		char buf[] = "adsdafeears2dfqqwe";
		int len = strlen(buf);

		mySort<char, int>(buf, len);
		myPrint<char, int>(buf, len);
	}
	
}

结果

排序之后......
53 43 23 11 9 4 3 1 w s s r q q f f e e e d d d a a a 2

3 函数模板 函数重载

模板匹配严格的匹配类型,不会进行自动转换

#include<iostream>
using namespace std;

//类型参数化 泛型编程
//template C++泛型编程
template <typename T>
void myswap(T &a, T &b) {
	T c = a;
	a = b;
	b = c;
	cout << "模板函数" << endl;
}

void myswap(int a, char c) {
	cout << "a:" << a << " c:" << c << endl;
	cout << "普通函数" << endl;
}

void main() {
	int		a = 10;
	char		c = 'z';
	myswap(a, c);	//	普通函数调用 可以进行隐式类型转换
	myswap(c, a);	//	
	
	myswap(a, a);	//	调用函数模板(本质:类型参数化),严格按照类型匹配,不会进行自动类型转换

}

4 函数模板和普通函数一起调用规则

  1. 函数模板可以重载(汇编过程中生成多个函数)
  2. c++编译器优先考虑普通函数
  3. 如果函数模板匹配更好,选择函数模板
  4. 通过空参数列表的方法限定编译器只通过模板匹配
#include<iostream>
using namespace std;

//函数模板可以重载
//c++编译器优先考虑普通函数
//如果函数模板匹配更好,选择函数模板
//通过空参数列表的方法限定编译器只通过模板匹配


int Max(int a, int b) {
	cout << "int Max(int a, int b)" << endl;
	return a > b ? a : b;
}

template<typename T>
T Max(T a, T b) {
	cout << "T Max(T a, T b)" << endl;
	return a > b ? a : b;
}

template<typename T>
T Max(T a, T b, T c) {
	cout << "T Max(T a, T b, T c)" << endl;
	return Max(Max(a, b), c);
}

void main() {
	int a = 1;
	int b = 2;
	cout << Max(a, b) << endl;	//函数模板和普通函数都符合是,优先普通函数
	cout << Max<>(a, b) << endl;	//若显示使用函数模板,则用<>类型列表

	cout << Max(3.0, 4.0) << endl;	// 如果函数模板产生更好匹配,优先使用函数模板

	cout << Max(5.0, 6.0, 7.0) << endl;	//重载

	cout << Max('a', 100) << endl;	//调用普通函数 隐式类型转换
}

结果

int Max(int a, int b)
2
T Max(T a, T b)
2
T Max(T a, T b)
4
T Max(T a, T b, T c)
T Max(T a, T b)
T Max(T a, T b)
7
int Max(int a, int b)
100

5 C++编译器模板机制

gcc编译器GNU compiler collection

gcc/g++编译过程

预处理(Pre-Processing)

编译(Compiling)

汇编(Assembling)

链接(Linking)

gcc *.c -o 1exe
gcc -E 1.c -o 1.i	// 宏定义 宏展开
gcc -S 1.i -o 1.s	// 汇编文件
gcc -c 1.s -o 1.o	// 可重定位目标文件
gcc 1.o -o 1exe		// 可执行目标文件
选项作用
-o产生目标(.i .s .o 可执行文件等)
-c取消链接步骤,编译源码并在最后生成目标文件
-E运行C预编译器
-S编译器产生汇编语言文件后停止编译,产生汇编语言文件扩展名为.s
-Wall使gcc对源文件的代码有问题地方发出警告
-ldir将dir目录加入搜索文件的目录路径
-Ldir将dir目录加入搜索库的目录路径
-llib链接lib库
-g在目标文件中嵌入调试信息,以便gdb调试
  • 编译器不是把函数模板处理成能够任意处理的函数
  • 编译器从函数模板通过具体类型产生不同函数
  • 编译器对函数模板进行两次编译:在声明地方对模板代码本身进行编译;在调用的地方对参数替换后的代码进行编译

2 类模板

1 类模板语法

数据结构和算法进行有效分离

#include<iostream>
using namespace std;

// 模板类 类型参数化



template<typename T>
class A {
public:
	A(T a = 0) {
		this->a = a;
	}
	void printA() {
		cout << "a: " << a << endl;
	}
private:
	T a;
};

//	类模板做函数参数
void UseA(A<int>& a) {
	a.printA();
}

void main() {
	// 模板类(本身是具体化的)===>具体的类===>定义具体变量
	A<int>a1(11), a2(20);	// 需要进行类型具体化
	a1.printA();

	UseA(a1);
	UseA(a2);
}

模板类派生普通类

template<typename T>
class A {
public:
	A(T a) {
		this->a = a;
	}
	void printA() {
		cout << "a: " << a << endl;
	}
protected:
	T a;
};

// 从模板类派生时需要具体化模板类,C++编译器需要知道父类的数据类型,需要知道父类内存大小
class B :public A<int> {
public:
	B(int a = 10, int b = 20) :A<int>(a) {
		this->b = b;
	}
	void printB() {
		cout << "a: " << a << endl;
		cout << "b: " << b << endl;
	}
private:
	int b;
};

void main() {
	B b1(1,2);
	b1.printB();
}

模板类派生模板类

template<typename T>
class A {
public:
	A(T a) {
		this->a = a;
	}
	void printA() {
		cout << "a: " << a << endl;
	}
protected:
	T a;
};
// 模板类派生模板类
template <typename T>
class C :public A<T> {
public:
	C(T c, T a) :A<T>(a) {
		this->c = c;
	}

	void printC() {
		cout << "c: " << c << endl;
	}
protected:
	T c;
};

void main() {
	C<int> c1(1, 2);
	c1.printC();
}

1类模板函数写在类的内部

#include<iostream>
using namespace std;

template <typename T>
class Complex {
	friend Complex MySub(Complex& c1, Complex& c2) {
		Complex tmp(c1.a - c2.a, c1.b - c2.b);
		return tmp;
	}
	
	friend ostream& operator<<(ostream& out, Complex& c) {
		out << c.a << " + " << c.b << "i" << endl;
		return out;
	}
public:
	Complex(T a, T b) {
		this->a = a;
		this->b = b;
	}

	Complex operator+(Complex& c) {
		/*this->a = this->a + c.a;
		this->b = this->b + c.b;*/
		Complex tmp(a + c.a, b + c.b);
		return tmp;
	}
	
	void printCom() {
		cout << a << " + " << b << "i";
	}

private:
	T a;
	T b;
};

// 运算符重载的正规写法
// 重载<< >> 用友元函数,其他运算符重载要写成成员函数,不要乱用友元函数


//ostream& operator<<(ostream& out, Complex& c) {
//	out << c.a << " + " << c.b << "i" << endl;
//	return out;
//}

void main() {
	// 需要把模板类进行具体化 才能定义对象,分配内存
	Complex<int> c1(1, 2);
	Complex<int> c2(3, 4);

	Complex<int> c3 = c1 + c2;

	cout << c3 << endl;


	// 滥用友元函数???
	{
		Complex<int> c4 = MySub(c2,c1);
		cout << c4 << endl;
	}

}

2类模板函数写在类的外部,写在一个cpp中

(超级麻烦)

构造函数没有问题,普通函数没有问题;

友元函数 重载<< >> friend ostream& operator<< ******(ostream& out, Complex& c); 声明时加

友元函数不是重载<< >> 时

  1. 需要添加类的前置声明
template <typename T>
Complex<T> MySub(Complex<T>& c1, Complex<T>& c2);

template <typename T>
class Complex {
	friend Complex MySub<T> (Complex& c1, Complex& c2);
  1. 类的内部声明必须写成
friend Complex<T> MySub<T> (Complex<T>& c1, Complex<T>& c2);
// friend Complex MySub<T> (Complex& c1, Complex& c2); 这样也行
  1. 友元实现必须写成
template<typename T>
Complex<T> MySub(Complex<T>& c1, Complex<T>& c2) {
	Complex<T> tmp(c1.a - c2.a, c1.b - c2.b);
	return tmp;
}
  1. 友元函数调用必须写成
// 滥用友元函数???
	{
		Complex<int> c4 = MySub<int>(c2,c1);
		cout << c4 << endl;
	}
#include<iostream>
using namespace std;



template <typename T>
class Complex; // 类的前置声明 

template <typename T>
Complex<T> MySub(Complex<T>& c1, Complex<T>& c2);

template <typename T>
class Complex {
	friend Complex MySub<T> (Complex& c1, Complex& c2);
	
	friend ostream& operator<< <T>(ostream& out, Complex<T>& c);	//operator<< 后加<T>
public:
	Complex(T a, T b);

	Complex operator+(Complex& c);
	Complex operator-(Complex& c);

	void printCom();

private:
	T a;
	T b;
};


// 构造函数写在类的外部
template <typename T>
Complex<T>::Complex(T a, T b) {
	this->a = a;
	this->b = b;
}

template <typename T>
Complex<T> Complex<T>::operator+(Complex<T>& c) {
	/*this->a = this->a + c.a;
	this->b = this->b + c.b;*/
	Complex tmp(a + c.a, b + c.b);
	return tmp;
}

template <typename T>
Complex<T> Complex<T>::operator-(Complex<T>& c) {
	Complex tmp(a - c.a, b - c.b);
	return tmp;
}

template <typename T>
void Complex<T>::printCom() {
	cout << a << " + " << b << "i";
}

// 友元函数实现<<重载
// 本质 模板两次编译,第一次模板生成的函数头和第二次生成的函数头不一样
//error LNK2019 : 无法解析的外部符号 "class std::basic_ostream<char,struct std::char_traits<char> > & __cdecl operator<<(class std::basic_ostream<char,struct std::char_traits<char> > &,class Complex<int> &)" (? ? 6@YAAAV?$basic_ostream@DU?$char_traits@D@std@@@std@@AAV01@AAV?$Complex@H@@@Z),该符号在函数 _main 中被引用
// 声明时,operator<< 后加<T>
template <typename T>
ostream& operator<< (ostream& out, Complex<T>& c) {
	out << c.a << " + " << c.b << "i" << endl;
	return out;
}

//*******************************
// 滥用友元函数
template<typename T>
Complex<T> MySub(Complex<T>& c1, Complex<T>& c2) {
	Complex<T> tmp(c1.a - c2.a, c1.b - c2.b);
	return tmp;
}

void main() {
	// 需要把模板类进行具体化 才能定义对象,分配内存
	Complex<int> c1(1, 2);
	Complex<int> c2(3, 4);

	Complex<int> c3 = c1 + c2;

	cout << c3 << endl;


	// 滥用友元函数???
	{
		Complex<int> c4 = MySub<int>(c2,c1);
		cout << c4 << endl;
	}

}

结论:不用友元函数

3类模板函数写在类的外部,写不同的.h .cpp中

complex.h

#pragma once
#include<iostream>
using namespace std;

template <typename T>
class Complex {
	// friend Complex MySub<T>(Complex& c1, Complex& c2);
	friend ostream& operator<< <T>(ostream& out, Complex<T>& c);	//operator<< 后加<T>
public:
	Complex(T a, T b);

	Complex operator+(Complex& c);
	Complex operator-(Complex& c);

	void printCom();

private:
	T a;
	T b;
};

complex.hpp

#include<iostream>
#include"complex.h"

using namespace std;

// 构造函数写在类的外部
template <typename T>
Complex<T>::Complex(T a, T b) {
	this->a = a;
	this->b = b;
}

template <typename T>
Complex<T> Complex<T>::operator+(Complex<T>& c) {
	/*this->a = this->a + c.a;
	this->b = this->b + c.b;*/
	Complex tmp(a + c.a, b + c.b);
	return tmp;
}

template <typename T>
Complex<T> Complex<T>::operator-(Complex<T> & c) {
	Complex tmp(a - c.a, b - c.b);
	return tmp;
}

template <typename T>
void Complex<T>::printCom() {
	cout << a << " + " << b << "i";
}


template <typename T>
ostream& operator<< (ostream & out, Complex<T> & c) {
	out << c.a << " + " << c.b << "i" << endl;
	return out;
}

complex_test.cpp

#include<iostream>
#include"complex.h"
#include"complex.hpp"
using namespace std;

void main() {
	// 需要把模板类进行具体化 才能定义对象,分配内存
	Complex<int> c1(1, 2);
	Complex<int> c2(3, 4);

	Complex<int> c3 = c1 + c2;

	cout << c3 << endl;
}

3 类模板中的static 关键字

不同类模板的static不公用

#include<iostream>
using namespace std;

template <typename T>
class AA {
public:
	static T m_a;
};

template <typename T>
T AA<T>::m_a = 0;

// 等价于
//class AA1 {
//public:
//	static int m_a;
//};
//int AA1::m_a = 0;
//
//class AA2 {
//public:
//	static char m_a;
//};
//char AA2::m_a = 0;



void main() {

	// 每一种类型的类使用自己的m_a

	AA<int> a1, a2, a3;
	a1.m_a = 10;
	a2.m_a++;
	a3.m_a++;
	cout << AA<int>::m_a << endl;

	AA<char> b1, b2, b3;
	b1.m_a = 'a';
	b2.m_a++;
	b3.m_a++;

	cout << AA<char>::m_a << endl;
}

4 类模板实例

设计一个myVector

MyVector.h

#pragma once
#include<iostream>
using namespace std;

template <typename T>
class MyVector {
	friend ostream& operator<< <T>(ostream& out, const MyVector<T>& myv);
public:
	MyVector(int size = 0);	//	构造函数
	MyVector(const MyVector& obj);		//copy构造函数

	~MyVector();	//析构函数
	T& operator[](int index);
	MyVector& operator=(MyVector& obj);

	int getLen() {
		return m_len;
	}

protected:
	T* m_space;
	int m_len;

};

MyVector.cpp

#include<iostream>
#include"MyVector.h"

template <typename T>
ostream& operator<< (ostream& out, const MyVector<T> &obj) {
	for (int i = 0; i < obj.m_len; i++) {
		out << obj.m_space[i] << " ";
	}
	out << endl;
	return out;
}


//	构造函数
template <typename T>
MyVector<T>::MyVector(int size) {
	m_space = new T[size];
	m_len = size;
}	

//copy构造函数
template <typename T>
MyVector<T>::MyVector(const MyVector& obj) {
	// 根据mv1大小分配内存拷贝数据
	m_len = obj.m_len;
	m_space = new T[m_len];

	// copy 拷贝
	for (int i = 0; i < m_len; i++) {
		m_space[i] = obj.m_space[i];
	}
}

//析构函数
template <typename T>
MyVector<T>::~MyVector() {
	if (m_space != NULL) {
		delete [] m_space;
		m_space = NULL;
		m_len = 0;
	}
}

template <typename T>
T& MyVector<T>::operator[](int index) {
	return m_space[index];
}

template <typename T>
MyVector<T>& MyVector<T>::operator=(MyVector<T>& obj){

	//先把旧内存释放掉	应该还要判断一下两个对象是不是同一个
	if (m_space != NULL) {
		delete[]m_space;
		m_space = NULL;
		m_len = 0;
	} 

	// 根据a1分配内存
	m_len = obj.m_len;
	m_space = new T[m_len];

	//copy
	for (int i = 0; i < m_len; i++) {
		m_space[i] = obj.m_space[i];
	}
	
	return *this;
}

MyVector_test.cpp

#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
#include"MyVector.cpp"
using namespace std;

class Teacher {
private:
	int age;
	char name[32];
public:
	Teacher() {
		age = 33;
	}
	Teacher(const char *name, int age) {
		this->age = age;
		strcpy(this->name, name);
	}
	void printT() {
		cout << name << "," << age << endl;
	}
};

void main() {
	Teacher t1("t1", 31), t2("t2", 32), t3("t3", 33), t4("t4", 34);

	MyVector<Teacher> tArray(4);
	tArray[0] = t1;
	tArray[1] = t2;
	tArray[2] = t3;
	tArray[3] = t4;
	for (int i = 0; i < 4; i++) {
		Teacher temp = tArray[i];
		temp.printT();
	}
}


void main02() {
	MyVector<char> myv1(10);

	for (int i = 0; i < myv1.getLen(); i++) {
		myv1[i] = 'a' + i ;
		cout << myv1[i] << " ";
	}
	cout << endl;
}

void main01() {
	MyVector<int> myv1(10);

	for (int i = 0; i < myv1.getLen(); i++) {
		myv1[i] = i + 1;
		cout << myv1[i] << " ";
	}
	cout << endl;

	MyVector<int> myv2 = myv1;
	for (int i = 0; i < myv2.getLen(); i++) {
		cout << myv2[i] << " ";
	}
	cout << endl;

	cout << myv2 << endl;

	
}

再加入一个Teacher类,如果该类中再有一个Teacher类,并且Teacher类的属性含有指针,会出现深拷贝和浅拷贝的问题

需要对Teacher封装的函数有:

  1. 重写拷贝构造函数
  2. 重载=操作符
  3. 重载<<操作符

MyVector_test.cpp

class Teacher {
private:
	friend ostream& operator<<(ostream& out, Teacher& t1);
	int age;
	char* pname;
public:
	Teacher() {
		pname = new char[1];
		strcpy(pname, "");
		age = 33;
	}
	Teacher(const char* name, int age) {
		if (name == NULL) {
			pname = new char[1];
			strcpy(pname, "");
		}
		else {
			pname = new char[strlen(name) + 1];
			strcpy(pname, name);
		}
		this->age = age;
	}
	Teacher(const Teacher& obj) {
		pname = new char[strlen(obj.pname) + 1];
		strcpy(pname, obj.pname);
		this->age = obj.age;
	}

	~Teacher() {
		if (pname != NULL) {
			delete[] pname;
			pname = NULL;
		}
		age = 0;
	}

	Teacher& operator=(Teacher& t1) {
		
		if (this->pname != NULL) {
			delete[] pname;
			pname = NULL;
		}
		pname = new char[strlen(t1.pname) + 1];
		this->age = t1.age;
		strcpy(this->pname , t1.pname);
		return *this;
	}
};

ostream& operator<<(ostream& out, Teacher& t1) {
	out << t1.pname << "," << t1.age;
	return out;
}
void main() {
	Teacher t1("t1", 31), t2("t2", 32), t3("t3", 33), t4("t4", 34);

	MyVector<Teacher> tArray(4);
	tArray[0] = t1;
	tArray[1] = t2;
	tArray[2] = t3;
	tArray[3] = t4;
	for (int i = 0; i < 4; i++) {
		cout << tArray[i] << endl;
		
	}
}

模板改成指针

void main() {
 	Teacher t1("t1", 31), t2("t2", 32), t3("t3", 33), t4("t4", 34);

	MyVector<Teacher*> tArray(4);
	tArray[0] = &t1;
	tArray[1] = &t2;
	tArray[2] = &t3;
	tArray[3] = &t4;
	for (int i = 0; i < 4; i++) {
		Teacher *temp = tArray[i];
		temp->printT();
	}
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值