泛化编程 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

DAY01 - 编程语言介绍

Day2 - Python模块-01

apriori片段代码

day06-jsp

day01_08.三大控制结构