15.模板

模板

1.目前函数的问题

以下函数存在的问题
1. 功能一致,但是函数参数类型不同
2. 按照目前的计数手段,只能分别实现不同的函数完成代码

3. 导致代码冗余,函数冗余,编译冗余

利用 C++ 模板解决

#include <iostream>

using namespace std;

void printValue(int num);
void printValue(double num);
void printValue(char ch);
void printValue(bool ret);
void printValue(string s);

int main(int argc, char const *argv[])
{   
    printValue(10);
    printValue(5.5);
    printValue('A');
    printValue(false);
    printValue("233333");

    return 0;
}

void printValue(int num) { cout << num << endl; }
void printValue(double num) { cout << num << endl; }
void printValue(char ch) { cout << ch << endl; }
void printValue(bool ret) { cout << ret << endl; }
void printValue(string s) { cout << s << endl; }

2.模板函数

什么是模板?

模板是泛型编程的基础,泛型编程是以一种特定类型的方式编写代码。

template

T 是函数所使用的数据类型的占位符名称。这个名称可以在函数定义中使用。

既可以定义函数模板,也可以定义类模板

类模板在类被实例化的时候进行指定。

template <typename T,typename K>

可以用逗号分隔,定义多个数据类型。

模板案例

#include <iostream>
#include <deque>
using namespace std;

/*
template 单词汉语对应就是模版
在这里自定义模版,设置模版数据类型为 T 类型
*/
template <typename T>
void printValue(T t);

int main(int argc, char const *argv[])
{
    printValue(10);
    printValue(5.5);
    printValue('A');
    printValue(false);
    printValue("233333");
    
    return 0;
}

template <typename T>
void printValue(T t)
{
    cout << "Value : " << t << endl;
}

3.模板函数细节

​ 函数存在重载情况,分别对应模板函数和 int 具体数据类型函数,编译器会优先选择具体数据类型明确的函数,调用实现
【先实后虚】【注意数据类型一致化原则】

#include <iostream>

using namespace std;

template <typename T>
void test(T t);

template <typename T>
void test(T t1, T t2);

void test(int n);

int main(int argc, char const *argv[])
{
    /*
    当前代码中,针对于 test 函数没有具体参数数据类型为 double 情况。
    编译器会选择模板函数为当前执行的目标,并且在当前函数调用的过程中
    明确模板对应的具体数据类型为 【double】 类型
    */
    test(20.5F);
    /*
    test 函数存在重载情况,分别对应模板函数和 int 具体数据类型函数,编译器会优先选择具体数据类型明确的函数,调用实现
    【先实后虚】
    */
    test(20);

    /*
    tempate <typename T>
    void teat(T t1,T t2)
        语法错误!!因为当前和散户有且只声明了一个 模板 T 要求
        在函数的执行过程中,T 类型唯一且统一
    test(10,20.5);(int,double)语法错误
    */
    test(10, 20);
    // test(10,'A');语法错误,请严格遵守【数据类型一致化原则】
    test('A', 'B');
    return 0;
}
template<typename T>
void test(T t)
{
    cout << "模板函数触发 value : " << t << endl;
}

void test(int n)
{
    cout << "具体参数为 int 类型函数触发 value : " << n << endl;
}

template<typename T>
void test(T t1,T t2)
{
    cout << "Value 1 : " << t1 << ",Value 2 : " << t2 << endl;
}

template<>

​ 告知编译器,当前函数需要使用模板函数形式没有明确的模板类型声明,函数名之后加入的中括号和具体数据类型
mySwap 明确告知编译器,当前模板对应的具体数据类型为 Person 类型
按照函数声明

template <>

void mySwap(Person &t1, Person &t2)

#include <iostream>

using namespace std;

class Person
{
public:
    Person() {}
    Person(int id, string name, int age) : id(id), name(name), age(age) {}
    Person(const Person &p) : id(p.id), name(p.name), age(p.age) {}
    ~Person() {}

    friend ostream &operator<<(ostream &o, Person *p);

    int id;
    string name;
    int age;
};

ostream &operator<<(ostream &o, Person *p)
{
    o << "ID : " << p->id << ",Name : " << p->name << ",Age : " << p->age;
    return o;
}
/*
函数中第一个参数 T1 t1 对应的是当前模板声明中的 T1
函数中第二个参数 T2 t2 对应的是当前模板声明中的 T2

都需要具体的实际参数类型来约束模板对应的具体数据类型
*/
template <typename T1, typename T2>
void test(T1 t1, T2 t2)
{
    cout << "T1 : " << t1 << "T2 : " << t2 << endl;
}

template <typename T>
void mySwap(T &t1, T &t2)
{
    T temp = t1;
    t1 = t2;
    t2 = temp;
}

/*
template<> 告知编译器,当前函数需要使用模板函数形式没有明确的模板类型声明
函数名之后加入的中括号和具体数据类型
    mySwap<Person> 明确告知编译器,当前模板对应的具体数据类型为 Person 类型
按照函数声明
    template <typename T>
    void mySwap(T &t1,T &t2);

    T 类型对应的具体数据类型在当前实现函数中为 Person 类型
    定制化完成 Person 类型的 Swap 交换操作
*/
template <>
void mySwap<Person>(Person &t1, Person &t2)
{
    int id = t1.id;
    string name = t1.name;
    int age = t1.age;

    t1.id = t2.id;
    t1.name = t2.name;
    t1.age = t2.age;

    t2.id = id;
    t2.name = name;
    t2.age = age;
}
int main(int argc, char const *argv[])
{
    // 模板对应的类型需要根据实际情况下来进行判断
    /*
    void test<int,int>(int t1,int t2)
    */
    test(10, 20);
    /*
    void test<int,char>(int t1,char t2)
    */
    test(10, 'A');

    int n1 = 10;
    int n2 = 20;

    mySwap(n1, n2);
    cout << "n1 : " << n1 << "n2 : " << n2 << endl;

    double d1 = 10.5;
    double d2 = 25.5;

    mySwap(d1, d2);
    cout << "d1 : " << d1 << "d2 : " << d2 << endl;

    cout << "+++++=====================" << endl;

    Person *p1 = new Person(1, "郭7", 20);
    Person *p2 = new Person(2, "哈登", 35);

    cout << p1 << endl;
    cout << p2 << endl;
    cout << "0----------------------------------------" << endl;
    mySwap(*p1, *p2);
    cout << p1 << endl;
    cout << p2 << endl;

    return 0;
}

4.模板类

模板类型充当数据占位符,在整个类内,成员变量,成员函数,构造哈桑农户都可以使用

template <typename NameType, typename AgeType>
class Person {};

当前 Person 声明了两个模板分别对应

​ NameType 模版类型,名称可以看出,用于约束成员变量 name 的类型
AgeType 模版类型,名称可以看出,用于约束成员变量 age 的类型

Person 类型带有模板修饰,需要在使用前明确模板对应的具体类型,模板具备数据类型的多样性,同时一旦确定模板类型,严格遵守数据类型一致化原则。

Person 类型确定模板对应的具体数据类型

格式:

​ 类名<模板对应具体数据类型> * 对象 = new 类型<模板对应具体数据类型>(…);

#include <iostream>

using namespace std;

/*
当前 Person 类型,声明了两个模板
    NameType 模板类型,名称可以看出用于约束成员变量 name 的类型
    AgeType 模板类型,名称可以看出用于约束成员变量 age 的类型
模板类型充当数据类型占位符,在整个类内,成员变量,成员函数,构造函数都可以使用
*/

template <typename NameType, typename AgeType>
class Person
{
public:
    Person() {}
    Person(NameType name, AgeType age) : name(name), age(age) {}
    Person(const Person &person) : name(person.name), age(person.age) {}
    ~Person() {}

    NameType getName() { return name; }
    void setName(NameType name) { this->name = name; }

    AgeType getAge() { return age; }
    void setAge(AgeType age) { this->age = age; }

private:
    NameType name;
    AgeType age;
};
int main(int argc, char const *argv[])
{
    /*
    Person 类型带有模板修饰,需要在使用之前明确模板对应的具体类型
    模板具备数据类型支持的多样性,同时一旦确定模板类型,严格遵守数据类型一致化
    原则
    Person 类型如何确定模板对应的具体数据类型
    格式:
        类名<模板对应具体数据类型> *对象 = new 类型 <模板对应具体数据类型>(...);
    Person 类型在没有约束模板具体类型的情况下,对应 C++ 编译器所见的类型为
        Person 类型在没有越是模板具体类型的情况下,对应 C++ 编译器所见的类型为
        Person<NameType,AgeType>
    */

    // p1 明确告知编译器 NameType ==> string AgeType ==> int
    // 在后续的代码中,通过 p1 调用 Person 类内函数按照以上规则完成
    Person<string, int> *p1 = new Person<string, int>();

    p1->setName("郭7");
    p1->setAge(18);

    cout << "Name : " << p1->getName() << ",Age : " << p1->getAge() << endl;

    
    Person<string, long> *p2 = new Person<string, long>();
    p2->setName("吴9");
    p2->setAge(13L);

    cout << "Name : " << p2->getName() << ",Age : " << p2->getAge() << endl;
  
    delete p1;
    delete p2;
    return 0;
}

5.模板参数限制

双重限制

  • 限制外部类型
  • 限制模板类型
#include <iostream>

using namespace std;

template <typename DataType, typename MsgType>
class Data
{
public:
    Data() {}
    Data(DataType value, MsgType msg) : value(value), msg(msg) {}
    Data(const Data &data) : value(data.value), msg(data.msg) {}
    ~Data() {}

    DataType getValue() { return value; }
    void setValue(DataType value) { this->value = value; }

    MsgType getMsg() { return msg; }
    void setMsg(MsgType msg) { this->msg = msg; }

private:
    DataType value;
    MsgType msg;
};

/*
当前 test1 函数明确告知调用者,当前所需的参数为 Data 类型
同时限制 Data 中对应的模板类型为 <int ,string>
*/
void test1(Data<int, string> *data);

/*
当前 test1 函数明确告知调用者,当前所需的参数为 Data 类型
同时限制 Data 中对应的模版类型为 <double, string>
*/
void test1(Data<double, string> *data);

int main(int argc, char const *argv[])
{
    // 双重限制
    Data<int, string> *d1 = new Data<int, string>(10, "什么鬼");
    Data<double, string> *d2 = new Data<double, string>(10.13, "什么鬼");
    Data<char, string> *d3 = new Data<char, string>('A', "什么鬼");

    test1(d1);
    test1(d2);

    /*
        test1 函数重载操作,所需参数有两种情况
            Data<int, string> *
            Data<double, string> *

        并没有支持
            Data<char, string> * 考虑到双重限制,分别对应外部类型和模版类型
            d3 无法作为函数 test1 的实际参数
            【注意】请严格遵守数据类型一致化原则
        */
    // test1(d3);
    delete d1;
    delete d2;
    delete d3;
    return 0;
}

void test1(Data<int, string> *data)
{
    cout << "Msg : " << data->getMsg() << ",Value : " << data->getValue() << endl;
}

void test1(Data<double, string> *data)
{
    cout << "Msg : " << data->getMsg() << ",Value : " << data->getValue() << endl;
}

6. 模板继承

类中带有模板,同时有子类继承当前类

​ 1.妻管严模式

​ 子类在继承父类时,直接指定模板对应的具体数据类型,不得修改

​ 2.自由模式

​ 子类带有和父类同名声明模板

6.1妻管严模式

template
class MyCompare{};

MyCompare 类
1.声明了模板。
2.声明纯虚函数
3.MyCompare 是一个抽象类。

class IntCompare : public MyCompare{};

IntCompare 类
1.继承了 MyCompara 抽象类
2.继承父类的过程中,同时限制了模板对应的具体类型
3.IntCpmpare 类没有声明模板

#include <iostream>

using namespace std;

/*
MyCompare 类
    1.声明了模板。
    2.声明纯虚函数
    3.MyCompare 是一个抽象类。
*/
template <typename T>
class MyCompare
{
public:
    // 纯虚函数,并且使用了类名声明的模板
    virtual bool compare(T t1, T t2) = 0;
};

/*
IntCompare 类
    1.继承了 MyCompara 抽象类
    2.继承父类的过程中,同时限制了模板对应的具体类型
    3.IntCpmpare 类没有声明模板
*/
class IntCompare : public MyCompare<int>
{
    public:
    bool compare(int t1,int t2)
    {
        return t1 > t2;
    }
};

int main(int argc, char const *argv[])
{
    IntCompare * ic = new IntCompare;
    cout << "ret : " << ic->compare(200,30) << endl;
    return 0;
}

6.2 自由模式

template
class BaseHandler

定义 BaseHandler 类
1.声明了模板 T
2.使用了模板 T
3.BaseHandler 是一个抽象类

template
class MyHandler : public BaseHandler{};

MyHandler 继承抽象类 Base Handler,同时
1.声明和 BaseHandeler 同名模板
2.当前模板类型尚未明确
3,必须实现 void handler(T t) 函数

实例化 MyHandler 类对象,需要指定模板对应的具体类型
MyHandler *m1 = new MyHandler;
m1->handler(“什么鬼”);

#include <iostream>

using namespace std;
/*
定义 BaseHandler 类
    1.声明了模板 T
    2.使用了模板 T
    3.BaseHandler 是一个抽象类
*/
template <typename T>
class BaseHandler
{
public:
    virtual void handler(T t) = 0;
};


/*
MyHandler 继承抽象类 Base Handler,同时
    1.声明和 BaseHandeler 同名模板
    2.当前模板类型尚未明确
    3,必须实现 void handler(T t) 函数
*/
template <typename T>
class MyHandler : public BaseHandler<T>
{
public:
    void handler(T t)
    {
        cout << "数据情况 : " << t << endl;
    }
};

int main(int argc, char const *argv[])
{
    // 实例化 MyHandler 类对象,需要指定模板对应的具体类型
    MyHandler<string> *m1 = new MyHandler<string>;
    m1->handler("什么鬼");

    MyHandler<int> *m2 = new MyHandler<int>;
    m2->handler(10006);
    return 0;
}

7.模板内的模板函数

template
class Test

{

​ template
void bt(T2 t) { cout << "value : " << t << endl;

};

模板内的函数,期望可以自定义模板,使用的模板数据形式,约束与当前类不同。

#include <iostream>

using namespace std;

template <typename T>
class Test
{
public:
    // 当前函数使用的模板为当前 Test 类声明的模板
    void handler(T t) { cout << "Value : " << t << endl; }

    /*
    模板类内的函数,期望可以自定义模板,使用的模板数据形式,约束与当前类不同
    */
    template <typename T2>
    void bt(T2 t) { cout << "Value : " << t << endl; }
};
int main(int argc, char const *argv[])
{
    Test<string> *test = new Test<string>;
    test->handler("没有边界感的烦人精!");
    /*
    bt 函数对应的模板类型,由实际参数决定,并不是实例化 Test 类型决定
    */
    test->bt(10);
    return 0;
}

8.返回值类型带有模板

template <typename K1,typename V1>
friend ostream & operator<<(ostream & o,Data<K1,V1> *data);

友元函数,重载运算符操作,使用的模板并不是 Data 声明的模板而是函数自定义模板,通过实际参数 Data 类型中的模板情况,限制当前函数模板的类型
【满足任意 Data 类型展示数据操作】

#include <iostream>

using namespace std;

template <typename K,typename V>
class Data 
{
 public:
    Data(){}
    Data(K key,V value) : key(key),value(value){}
    Data(const Data & data):key(data.key),value(data.value){}
    ~Data(){}

    K getKey(){return key;}
    void setKey(K key){this->key = key;}

    V getValue(){return value;}
    void setValue(){this->value = value;}

    /*
    友元函数,重载运算符操作,使用的模板并不是 Data 声明的模板而是函数自定义模板,通过实际参数 Data 类型中的模板情况,限制当前函数模板的类型
    【满足任意 Data 类型展示数据操作】
    */
    template <typename K1,typename V1>
    friend ostream & operator<<(ostream & o,Data<K1,V1> *data);
    private:
    K key;
    V value;
};

template<typename K1,typename V1>
ostream & operator<<(ostream & o,Data<K1,V1> * data)
{
    o << "key : " << data->getKey() << ",value : " << data->getValue();
    return o;
}

template <typename K,typename V>
Data<K,V> * getData(K key,V value);

int main(int argc, char const *argv[])
{
    string name = "BT";
    Data<string , int> * data = getData(name,1096);
    cout << data << endl;

    Data<double,int> * data2 = getData(1.24,1039);
    cout << data2 << endl;

    Data<string,int> *data3 = new Data<string,int>("snns",10);
    cout << data3 << endl;
    delete data;
    delete data2;
    return 0;
}
/*
外部函数自定义模板,返回值是 Data 类型,同时利用当前函数的实际参数对 Data
中的模板数据进行约束限制,模板具体数据情况参数具体数据类型确定。
*/
template <typename K,typename V>
Data<K,V> * getData(K key,V value)
{
    return new Data<K,V>(key,value);
}

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值