泛型编程
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 函数模板和普通函数一起调用规则
- 函数模板可以重载(汇编过程中生成多个函数)
- c++编译器优先考虑普通函数
- 如果函数模板匹配更好,选择函数模板
- 通过空参数列表的方法限定编译器只通过模板匹配
#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); 声明时加
友元函数不是重载<< >> 时
- 需要添加类的前置声明
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 Complex<T> MySub<T> (Complex<T>& c1, Complex<T>& c2);
// friend Complex MySub<T> (Complex& c1, Complex& c2); 这样也行
- 友元实现必须写成
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;
}
- 友元函数调用必须写成
// 滥用友元函数???
{
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封装的函数有:
- 重写拷贝构造函数
- 重载=操作符
- 重载<<操作符
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();
}
}