泛化编程 day01
Posted 达少Rising
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了泛化编程 day01相关的知识,希望对你有一定的参考价值。
一、模板的起源
1、C/C++是静态的语言(编译型语言)
- 1)这类语言有很多的数据类型(int/double/float等等…)在效率和安全性的方面是无可比拟的
- 2)这类语言在很大程度上也给程序员编写
通用代码
带来瓶颈,使程序员不得不为每一种数据类型
编写完全相同或几乎完全相同的代码实现
,虽然他们在抽象层面是一致的
eg:type01.cpp
#include <iostream>
using namespace std;
int max_int(int x, int y)
{
return x > y ? x : y;
}
double max_double(double x, double y)
{
return x > y ? x : y;
}
string max_string(string x, string y)
{
return x > y ? x : y;
}
int main(void)
{
int nx=10, ny=20;
cout << max_int(nx, ny) << endl;
double dx=12.3, dy=45.6;
cout << max_double(dx, dy) << endl;
string sx="hello", sy="world";
cout << max_string(sx, sy) << endl << endl;
return 0;
}
2、借助参数宏可以摆脱数据类型的限制
- 1)宏只是在预处理阶段针对代码的
纯文本替换
- 2)宏本身没有函数的语义(
不会对数据类型进行检查
) - 3)因此借助参数宏虽然可以拜托类型的约束和限制,但同时也丧失了对
数据类型的检查
eg:untype02.cpp
#include <iostream>
using namespace std;
#define Max(x, y) (x > y ? x : y)
int main(void)
{
int nx=10, ny=20;
cout << Max(nx, ny) << endl;
//cout << (nx>ny?nx:ny) << endl;//純文本類型替換
double dx=12.3, dy=45.6;
cout << Max(dx, dy) << endl;
string sx="hello", sy="world";
cout << Max(sx, sy) << endl;//world
char cx[256]="world", cy[256]="hello";
cout << Max(cx, cy) << endl;//hello
//cout << (cx>cy?cx:cy) << endl;//比較的是地址誰大
return 0;
}
3、借助宏构建通用函数的框架
- 1)通过
实例化宏
,让预处理将这个宏扩展针对不同数据类型
的真正函数 - 2)将
宏的通用性
和函数的类型安全性
完美结合起来
eg:marco03.cpp
#include <iostream>
using namespace std;
//##是拼接
#define MAX(T) T max_##T(T x, T y){\\
return x > y ? x : y;\\
}
MAX(int)
//int max_int(int x, int y){return x>y ? x:y;}
MAX(double)
//int max_dooublc(double x, double y){...}
MAX(string)
//string max_string(string x, string y){...}
int main(void)
{
int nx=10, ny=20;
cout << max_int(nx, ny) << endl;
double dx=12.3, dy=45.6;
cout << max_double(dx, dy) << endl;
string sx="world", sy="hello";
cout << max_string(sx, sy) << endl;
return 0;
}
eg:macro03_02.cpp
#include <iostream>
using namespace std;
//##是拼接
#define MAX(T) T max_##T(T x, T y){\\
return x > y ? x : y;\\
}
MAX(int)
//int max_int(int x, int y){return x>y ? x:y;}
MAX(double)
//int max_dooublc(double x, double y){...}
MAX(string)
//string max_string(string x, string y){...}
#define Max(T) max_##T
int main(void)
{
int nx=10, ny=20;
cout << MAX(int)(nx, ny) << endl;
double dx=12.3, dy=45.6;
cout << MAX(double)(dx, dy) << endl;
string sx="world", sy="hello";
cout << MAX(string)(sx, sy) << endl;
return 0;
}
二、函数模板
1、函数模板声明
- 1)函数模板的声明形式:
template<class 类型形参1, class 类型形参2, ...>
返回值类型 函数模板名(调用形参1, 调用形参2, ...){
...
}
eg:
template<class T>T Max(T x, T y){
return x>y?x:y;
}
可以使用任何标识符
作为类型形参的名称,但使用"T"已经成为一种惯例,"T"表示的是调用者在使用这个函数模板时指定的任意数据类型
。
2、函数模板的使用
- 使用函数模板
必须对模板进行实例化
- 形式:
函数模板名<类型实参1, 类型实参2,...>(调用实参1,...);
eg:
Max<int>(123, 456);
Max<double>(12.3,45.6);
Max<string>("hello", "world");
3、分析函数模板
- 切记编译器并不把函数模板编译成一个可以处理任何数据类型的
单一实体
,而应该是编译器在实例化函数模板时根据类型实参
从函数模板中产生一个真正的函数实体。(注:函数模板并不是一个函数实体
,通过实例化产能产生真正的函数实体,函数模板可以认为是编译器生产函数实体的一个依据而已
) - 这种用
具体数据类型
替换函数模板类型形参
的过程叫做实例化,这个过程产生一个函数模板的实例(函数实体)。 - 只是使用函数模板,就会自动引发编译器实例化过程,因此
程序员不需要额外地请求对函数模板的实例化
。
eg:functmpl04.cpp
#include <iostream>
using namespace std;
//函數模板(函數模板並不是一個函數)
template<class T>T Max(T x, T y){
return x > y ? x : y;
}
/*
Max<int>等價
int Max(int x, int y){return x>y?x:y;}
*/
int main(void)
{
int nx=10, ny=20;
cout << Max<int>(nx, ny) << endl;
//cout << Max(nx, ny) << endl;
double dx=12.3, dy=45.6;
cout << Max<double>(dx, dy) << endl;
string sx="world", sy="hello";
cout << Max<string>(sx, sy) << endl;
return 0;
}
4、函数模板的扩展
- 可以使用
任何数据类型
(基本类型和类类型)实例化函数模板 - 但前提是这个数据类型必须支持函数模板所要执行的操作
例如:一个不支持“>”运算符操作的类型,实例化Max函数模板,编译器将报错。
eg:functmpl04_02.cpp
#include <iostream>
using namespace std;
class Integer{
public:
Integer(int const& i=0):m_i(i){}
bool operator>(Integer const& that)const{
return m_i > that.m_i;
}
friend ostream& operator<<(ostream& os, Integer const& that){
return os << that.m_i;
}
private:
int m_i;
};
//函數模板(函數模板並不是一個函數)
template<class T>T Max(T x, T y){
return x > y ? x : y;
}
/*
Max<int>等價
int Max(int x, int y){return x>y?x:y;}
*/
int main(void)
{
int nx=10, ny=20;
cout << Max<int>(nx, ny) << endl;
//cout << Max(nx, ny) << endl;
double dx=12.3, dy=45.6;
cout << Max<double>(dx, dy) << endl;
string sx="world", sy="hello";
cout << Max<string>(sx, sy) << endl;
Integer ix=100, iy=200;
//在沒有重載>的時候下面的語句會報錯
//functmpl04_02.cpp:13:11: error: no match for ‘operator>’ (operand types are ‘Integer’ and ‘Integer’)
//重載>後就不會報錯
cout << Max<Integer>(ix, iy) << endl;
//Integer需要重載<<運算符
return 0;
}
5、二次编译:编译器对模板都会进行两次编译
- 第一次编译发生在实例化函数模板之前(
产生真正函数实体之前
),先检查模板本身内部代码,查看基本词法是否正确(例如:函数模板内部出现的所有标识符是否均有出处)对于已知类型的调用要检查调用是否有效
,对于未知类型调用都合理
- 第二次发生在实例化之后(
产生真正函数实体之后
),结合所有使用的类型实参,再次检查模板代码,查看所有调用是否均有效。
eg:05complie.cpp
#include <iostream>
using namespace std;
class Integer
{
public:
void foo(){
cout << "Integer::foo()" << endl;
}
int m_i;
};
template<class T>void Max(){
//一下都是第一次編譯
//abcd;//亂寫的內容會報錯
Integer i;//對已知類型的調用
i.foo();//調用合理
//i.abcd();//不合理的調用會報錯
T t;//對未知類型的調用
t.abcd();//調用合理,第二次不合理
//t.ab<c>d();//出現括號也會報錯
}
int main(void)
{
//函數模板實例化,第二次編譯,未知類型調用未知的調用會報錯
Max<Integer>();
return 0;
}
6、函数模板的隐式推断
- 如果函数模板的
调用形参
和类型形参
相关
例如:template<class T>T Max(T x, T y){...}
- 那么在实例化函数模板时即使不显式指明函数模板的
类型实参
,编译器也有能力根据调用实参的类型
隐式推断出正确的类型实参的类型
例如:Max(123, 456)->Max<>(123, 456)->Max<int>(123, 456);
- 获得和调用普通函数一致的语法表现形式
eg:06deduction.cpp
#include <iostream>
using namespace std;
class Integer{
public:
Integer(int const& i=0):m_i(i){}
bool operator>(Integer const& that)const{
return m_i > that.m_i;
}
friend ostream& operator<<(ostream& os, Integer const& that){
return os << that.m_i;
}
private:
int m_i;
};
//函數模板(函數模板並不是一個函數)
template<class T>T Max(T x, T y){
return x > y ? x : y;
}
/*
Max<int>等價
int Max(int x, int y){return x>y?x:y;}
*/
int main(void)
{
int nx=10, ny=20;
cout << Max<>(nx, ny) << endl;
//也可以寫成Max(nx, ny), 過程Max<>(nx, ny)==>Max<int>(nx, ny)
double dx=12.3, dy=45.6;
cout << Max<>(dx, dy) << endl;
string sx="world", sy="hello";
cout << Max<>(sx, sy) << endl;
Integer ix=100, iy=200;
//在沒有重載>的時候下面的語句會報錯
//functmpl04_02.cpp:13:11: error: no match for ‘operator>’ (operand types are ‘Integer’ and ‘Integer’)
//重載>後就不會報錯
cout << Max<>(ix, iy) << endl;
//Integer需要重載<<運算符
return 0;
}
7、三种情况不能做隐式推断
- 调用参数和类型参数不能完全相关
eg:template<class T, class D>T Max(T x, T y){}
- 隐式推断不支持隐式类型转换
eg:template<class T>T Max(T x, T y){...}
使用时:Max(123, 45.6);
- 返回值类型不支持隐式推断
eg:06deduction_02.cpp
#include <iostream>
using namespace std;
class Integer{
public:
Integer(int const& i=0):m_i(i){}
bool operator>(Integer const& that)const{
return m_i > that.m_i;
}
friend ostream& operator<<(ostream& os, Integer const& that){
return os << that.m_i;
}
private:
int m_i;
};
template<class T>T Max(T x, T y){
//調用行參和類型形參完全相關
return x > y ? x : y;
}
template<class D, class T>void Foo(T x){
//調用行參和類型形參不完全相關
}
template<class R, class T>R Bar(T x){
R r;
return r;
}
int main(void)
{
int nx=10, ny=20;
cout << Max<>(nx, ny) << endl;
//也可以寫成Max(nx, ny), 過程Max<>(nx, ny)==>Max<int>(nx, ny)
double dx=12.3, dy=45.6;
cout << Max<>(dx, dy) << endl;
string sx="world", sy="hello";
cout << Max<>(sx, sy) << endl;
Integer ix=100, iy=200;
cout << Max<>(ix, iy) << endl;
//1)
//Foo(nx);//報錯,不能隱式推斷
Foo<double>(nx);//不報錯可以推斷T的類型是int,因爲顯式指定了D爲double
//2)
//Max(nx,dy);//報錯
Max(nx,static_cast<int>(dy));//顯式轉換後不會報錯
//3)
//Bar(nx);//報錯,無法推斷返回值類型
Bar<double>(nx);//顯式給出R類型爲double
return 0;
}
8、函数模板的重载
- 普通函数和
可实例化出该函数的函数模板
构成重载关系。在数据类型匹配度
相同情况下编译器优先选择普通函数。除非函数模板可以产生更好的数据类型匹配度的实例。 函数模板的实例化不支持隐式类型转换
但普通函数支持。在传递参数时如果需要编译器做隐式类型转换,则编译器选择普通函数。- 可以在实例化时用<>强行通知编译器选择函数模板。
- 但是如果让编译器隐式
推断类型
,编译器让然坚持选择约束性较强的版本(即更特殊的版本)。
eg:07overload.cpp
#include <iostream>
using namespace std;
void Max(int x, int y)
{
cout << "1:Max(int, int)" << endl;
}
template<class T>void Max(T x, T y)
{
cout << "2:Max(T, T)" << endl;
}
template<class T>void Max(T* x, T* y)
{
cout << "3:Max(T*, T*)" << endl;
}
int main(void)
{
int nx=10, ny=20;
double dx=12.3, dy=45.6;
//1)
Max(nx, ny);//選擇普通函數
Max(dx, ny);//選擇函數模板
//2)
Max(nx, dy);//選擇普通函數
//3)
Max<>(nx, ny);//強行通知選擇模板
//4)
Max<>(&nx, &ny);//選擇模板2
return 0;
}
三、类模板
1、类模板的定义
- 在类模板内部
类型实参
可以想其他类型一样,用于成员变量,成员函数,成员类型(内部类),甚至基类声明
.
eg:
template<class A, class B>
class CMath{
public:
A m_a;
B func(){...};
};
- 如果在类外实现成员函数:
template<class 类型参数1,...>
返回值类型 类模板名<类型形参1,...>::函数名(调用形参1,...){
函数体实现;
}
例如:
template<class A, class B>B CMath<A, B>::func(){...;}
2、类模板的使用
- 1)使用类模板必须对类模板进行实例化(
产生真正的类
)。类模板本身并不代表一个确定的类型
(即不能用于定义对象),只有通过类型实例化成真正的类后才具备类的语义(即可以定义对象)。
eg:08clstemplate.cpp
#include <iostream>
using namespace std;
//類模板(並非真正的類)
template<class T>
class CMath
{
public:
CMath(T const& t1, T const& t2):m_t1(t1), m_t2(t2){}
T sum(){
return m_t1 + m_t2;
}
private:
T m_t1;
T m_t2;
};
//實例化的原理
//class CMatn<int>{...}
int main(void)
{
int nx=10, ny=20;
//實例化類模板
CMath<int泛化编程 day02