林西网站建设优化,常州网站制作包括哪些,网络营销论文范文,建网页软件模板概论 c提供了函数模板 (function template.) 所谓函数模板#xff0c;实际上是建立一个通用函 数#xff0c;其函数类型和形参类型不具体制定#xff0c;用一个虚拟的类型来代表。这个通用函数 就成为函数模板。凡是函数体相同的函数都可以用这个模板代替#xff0c;不…模板概论 c提供了函数模板 (function template.) 所谓函数模板实际上是建立一个通用函 数其函数类型和形参类型不具体制定用一个虚拟的类型来代表。这个通用函数 就成为函数模板。凡是函数体相同的函数都可以用这个模板代替不必定义多个函 数只需在模板中定义一次即可。在调用函数时系统会根据实参的类型来取代模板 中的虚拟类型从而实现不同函数的功能。 c 提供两种模板机制 : 函数模板和类模板 类属 - 类型参数化又称参数模板 总结 模板把函数或类要处理的数据类型参数化表现为参数的多态性成为类属。 模板用于表达逻辑结构相同但具体数据元素类型不同的数据对象的通用行为。 函数模板 //交换 int 数据
void SwapInt(int a,int b){int temp a;a b;b temp;
}
//交换 char 数据
void SwapChar(char a,char b){char temp a;a b;b temp;
}
//问题如果我要交换 double 类型数据那么还需要些一个 double 类型数据交换的函
数
//繁琐写的函数越多当交换逻辑发生变化的时候所有的函数都需要修改无形当中
增加了代码的维护难度
//如果能把类型作为参数传递进来就好了传递 int 就是 Int 类型交换传递 char 就是char 类型交换
//我们有一种技术可以实现类型的参数化---函数模板
//class 和 typename 都是一样的用哪个都可以
templateclass T
void MySwap(T a,T b){T temp a;a b;b temp;
}
void test01(){int a 10;int b 20;cout a: a b: b endl;//1. 这里有个需要注意点函数模板可以自动推导参数的类型MySwap(a,b);cout a: a b: b endl;char c1 a;char c2 b;cout c1: c1 c2: c2 endl;//2. 函数模板可以自动类型推导那么也可以显式指定类型MySwapchar(c1, c2);cout c1: c1 c2: c2 endl;
} 用模板是为了实现泛型可以减轻编程的工作量增强函数的重用性。 函数模板和普通函数区别 函数模板不允许自动类型转化 普通函数能够自动进行类型转化 //函数模板
templateclass T
T MyPlus(T a, T b){T ret a b;return ret;
}
//普通函数
int MyPlus(int a,char b){int ret a b;return ret;
}
void test02(){int a 10;char b a;//调用函数模板严格匹配类型MyPlus(a, a);MyPlus(b, b);//调用普通函数MyPlus(a, b);//调用普通函数 普通函数可以隐式类型转换MyPlus(b, a);//结论//函数模板不允许自动类型转换必须严格匹配类型//普通函数可以进行自动类型转换
} 函数模板和普通函数在一起调用规则 c 编译器优先考虑普通函数 可以通过空模板实参列表的语法限定编译器只能通过模板匹配 函数模板可以像普通函数那样可以被重载 如果函数模板可以产生一个更好的匹配那么选择模板 //函数模板
templateclass T
T MyPlus(T a, T b){T ret a b;return ret;
}
//普通函数
int MyPlus(int a, int b){int ret a b;return ret;
}
void test03(){int a 10;int b 20;char c a;char d b;//如果函数模板和普通函数都能匹配c编译器优先考虑普通函数cout MyPlus(a, b) endl;//如果我必须要调用函数模板,那么怎么办?cout MyPlus(a, b) endl;//此时普通函数也可以匹配因为普通函数可以自动类型转换//但是此时函数模板能够有更好的匹配//如果函数模板可以产生一个更好的匹配那么选择模板cout MyPlus(c,d);
}
//函数模板重载
templateclass T
T MyPlus(T a, T b, T c){T ret a b c;return ret;
}
void test04(){int a 10;int b 20;int c 30;cout MyPlus(a, b, c) endl;//如果函数模板和普通函数都能匹配c编译器优先考虑普通函数
} 模板机制剖析 思考 : 为什么函数模板可以和普通函数放在一起 ?c 编译器是如何实现函数模板机 制的 编译过程 hello.cpp 程序是高级 c 语言程序这种程序易于被人读懂。为了在系统上运行 hello.c 程序每一条 c 语句都必须转化为低级的机器指令。然后将这些机器指令 打包成可执行目标文件格式并以二进制形式存储于磁盘中。 预处理(Pre-processing) - 编译 (Compiling) - 汇编 (Assembling) - 链接 (Linking) 模板实现机制 函数模板机制结论 编译器并不是把函数模板处理成能够处理任何类型的函数 函数模板通过具体类型产生不同的函数 编译器会对函数模板进行两次编译在声明的地方对模板代码本身进行编译在 调用的地方对参数替换后的 代码进行编译。 模板的局限性 假设有如下模板函数 template class T void f(T a, T b) { … } 如果代码实现时定义了赋值操作 a b 但是 T 为数组这种假设就不成立了 同样如果里面的语句为判断语句 if(ab), 但 T 如果是结构体该假设也不成立 另外如果是传入的数组数组名为地址因此它比较的是地址而这也不是我们所 希望的操作。 总之编写的模板函数很可能无法处理某些类型另一方面有时候通用化是有 意义的但 C 语法不允许这样做。为了解决这种问题可以提供模板的重载为这些特定的类型提供具体化的模板。 class Person
{
public:Person(string name, int age){this-mName name;this-mAge age;}string mName;int mAge;
};
//普通交换函数
template class T
void mySwap(T a,T b)
{T temp a;a b; b temp;
}
//第三代具体化显示具体化的原型和定意思以 template开头并通过名称来指出类
型
//具体化优先于常规模板
templatevoid mySwapPerson(Person p1, Person p2)
{string nameTemp;int ageTemp;nameTemp p1.mName;p1.mName p2.mName;p2.mName nameTemp;ageTemp p1.mAge;p1.mAge p2.mAge;p2.mAge ageTemp;
}
void test()
{Person P1(Tom, 10);Person P2(Jerry, 20);cout P1 Name P1.mName P1 Age P1.mAge endl;cout P2 Name P2.mName P2 Age P2.mAge endl;mySwap(P1, P2);cout P1 Name P1.mName P1 Age P1.mAge endl;cout P2 Name P2.mName P2 Age P2.mAge endl;
} 类模板 类模板基本概念 类模板和函数模板的定义和使用类似我们已经进行了介绍。有时有两个或多个 类其功能是相同的仅仅是数据类型不同。 类模板用于实现类所需数据的类型参数化 templateclass NameType, class AgeType
class Person
{
public:Person(NameType name, AgeType age){this-mName name;this-mAge age;}void showPerson(){cout name: this-mName age: this-mAge endl;}
public:NameType mName;AgeType mAge;
};
void test01()
{//Person P1(德玛西亚,18); // 类模板不能进行类型自动推导Personstring, intP1(德玛西亚, 18);P1.showPerson();
} 类模板做函数参数 //类模板
templateclass NameType, class AgeType
class Person{
public:Person(NameType name, AgeType age){this-mName name;this-mAge age;}
void PrintPerson(){cout Name: this-mName Age: this-mAge endl;
}
public:NameType mName;AgeType mAge;
};
//类模板做函数参数
void DoBussiness(Personstring,int p){p.mAge 20;p.mName _vip;p.PrintPerson();
}
int main(){Personstring, int p(John, 30);DoBussiness(p);system(pause);return EXIT_SUCCESS;
} 类模板派生普通类 //类模板
templateclass T
class MyClass{
public:MyClass(T property){this-mProperty property;}
public:T mProperty;
};
//子类实例化的时候需要具体化的父类子类需要知道父类的具体类型是什么样的
//这样 c编译器才能知道给子类分配多少内存
//普通派生类
class SubClass : public MyClassint{
public:SubClass(int b) : MyClassint(20){this-mB b;}
public:int mB;
};
1.7.4 类模板派生类模板
//父类类模板
templateclass T
class Base
{T m;
};
templateclass T
class Child2 : public Basedouble //继承类模板的时候必须要确定基类的大小
{
public:T mParam;
};
void test02()
{Child2int d2;
} 类模板类内实现 templateclass NameType, class AgeType
class Person
{
public:Person(NameType name, AgeType age){this-mName name;this-mAge age;}void showPerson(){cout name: this-mName age: this-mAge endl;}
public:NameType mName;AgeType mAge;
};
void test01()
{//Person P1(德玛西亚,18); // 类模板不能进行类型自动推导Personstring, intP1(德玛西亚, 18);P1.showPerson();
} 类模板类外实现 #define _CRT_SECURE_NO_WARNINGS
#includeiostream
#includestring
using namespace std;
templateclass T1, class T2
class Person{
public:Person(T1 name, T2 age);void showPerson();
public:T1 mName;T2 mAge;
};
//类外实现
templateclass T1, class T2
PersonT1, T2::Person(T1 name, T2 age){this-mName name;this-mAge age;
}
templateclass T1, class T2void PersonT1, T2::showPerson(){cout Name: this-mName Age: this-mAge endl;
}
void test()
{Personstring, int p(Obama, 20);p.showPerson();
}
int main(){test();system(pause);return EXIT_SUCCESS;
} 类模板头文件和源文件分离问题 Person.hpp #pragma once
templateclass T1,class T2
class Person{
public:Person(T1 name,T2 age);void ShowPerson();
public:T1 mName;T2 mAge;
};
templateclass T1, class T2
PersonT1, T2::Person(T1 name, T2 age){this-mName name;this-mAge age;
}
templateclass T1, class T2
void PersonT1, T2::ShowPerson(){cout Name: this-mName Age: this-mAge endl;
} main.cpp #define _CRT_SECURE_NO_WARNINGS
#includeiostream
using namespace std;
#includestring
#includePerson.hpp
//模板二次编译
//编译器编译源码 逐个编译单元编译的
int main(){Personstring, int p(Obama, 20);p.ShowPerson();system(pause);return EXIT_SUCCESS;
} 结论 : 案例代码在 qt 编译器顺利通过编译并执行但是在 Linux 和 vs 编辑器下如 果只包含头文件那么会报错链接错误需要包含 cpp 文件但是如果类模板中 有友元类那么编译失败 解决方案 : 类模板的声明和实现放到一个文件中我们把这个文件命名为 .hpp( 这个 是个约定的规则并不是标准必须这么写). 原因 类模板需要二次编译在出现模板的地方编译一次在调用模板的地方再次编译。 C编译规则为独立编译。 模板类碰到友元函数 #define _CRT_SECURE_NO_WARNINGS
#includeiostream
using namespace std;
#include string
templateclass T1, class T2 class Person;
//告诉编译器这个函数模板是存在
templateclass T1, class T2 void PrintPerson2(PersonT1, T2 p);
//友元函数在类内实现
templateclass T1, class T2
class Person{//1. 友元函数在类内实现friend void PrintPerson(PersonT1, T2 p){cout Name: p.mName Age: p.mAge endl;
}
//2.友元函数类外实现
//告诉编译器这个函数模板是存在
friend void PrintPerson2(PersonT1, T2 p);
//3. 类模板碰到友元函数模板
templateclass U1, class U2
friend void PrintPerson(PersonU1, U2 p);
public:Person(T1 name, T2 age){this-mName name;this-mAge age;}void showPerson(){cout Name: this-mName Age: this-mAge endl;}
private:T1 mName;T2 mAge;
};
void test01()
{Person string, intp(Jerry, 20);PrintPerson(p);
}
// 类模板碰到友元函数
//友元函数类外实现 加上空参数列表告诉编译去匹配函数模板
templateclass T1 , class T2
void PrintPerson2(PersonT1, T2 p)
{cout Name2: p.mName Age2: p.mAge endl;
}
void test02()
{Person string, intp(Jerry, 20);PrintPerson2(p); //不写可以编译通过写了之后会找 PrintPerson2 的普通函数调用因为写了普通函数 PrintPerson2 的声明
}
int main(){//test01();test02();system(pause);return EXIT_SUCCESS;
} 类模板的应用 设计一个数组模板类 (MyArray), 完成对不同类型元素的管理 #pragma once
templateclass T
class MyArray
{
public:explicit MyArray(int capacity){this-m_Capacity capacity;this-m_Size 0;// 如果 T 是对象那么这个对象必须提供默认的构造函数pAddress new T[this-m_Capacity];}//拷贝构造MyArray(const MyArray arr){this-m_Capacity arr.m_Capacity;this-m_Size arr.m_Size;this-pAddress new T[this-m_Capacity];for (int i 0; i this-m_Size;i){this-pAddress[i] arr.pAddress[i];}}//重载[] 操作符 arr[0]T operator [](int index){return this-pAddress[index]; }//尾插法void Push_back(const T val){if (this-m_Capacity this-m_Size){return;}this-pAddress[this-m_Size] val;this-m_Size;}void Pop_back(){if (this-m_Size 0){return;}this-m_Size--;}int getSize(){return this-m_Size;}//析构~MyArray(){if (this-pAddress ! NULL){delete[] this-pAddress;this-pAddress NULL;this-m_Capacity 0;this-m_Size 0;}}
private:T * pAddress; //指向一个堆空间这个空间存储真正的数据int m_Capacity; //容量int m_Size; // 大小
}; 测试代码 class Person{
public:Person(){}Person(string name, int age){this-mName name;this-mAge age;}
public:string mName;int mAge;
};
void PrintMyArrayInt(MyArrayint arr){for (int i 0; i arr.getSize(); i){cout arr[i] ;}cout endl;
}
void PrintMyPerson(MyArrayPerson personArr)
{for (int i 0; i personArr.getSize(); i){cout 姓名 personArr[i].mName 年龄 personArr[i].mAge endl;}
}
void test01()
{MyArrayint myArrayInt(10);for (int i 0; i 9; i){myArrayInt.Push_back(i);}myArrayInt.Push_back(100);PrintMyArrayInt(myArrayInt);MyArrayPerson myArrayPerson(10);Person p1(德玛西亚, 30);Person p2(提莫, 20);Person p3(孙悟空,18);Person p4(赵信, 15);Person p5(赵云, 24);myArrayPerson.Push_back(p1);myArrayPerson.Push_back(p2);myArrayPerson.Push_back(p3);myArrayPerson.Push_back(p4);myArrayPerson.Push_back(p5);
}