本文介绍: 模板是一种较为通用的模具,不能单独使用C++除了面向对象编程之外,还有一种是泛型编程使用的就是模板C++中提供了两种模板:函数模板,类模板。

1 概述

模板是一种较为通用的模具,不能单独使用
C++除了面向对象编程之外,还有一种是泛型编程,使用的就是模板
C++中提供了两种模板:函数模板,类模板

2 函数模板

2.1 使用数模

数模板的作用是建立一个通用的函数,其返回值类型和形参类型可以不具体定制,用一个虚拟类型来代表
语法

template<typename T>
函数

template声明创建模板
typename – 表明其后面的符号是一种数据类型可以class代替
T – 通用数据类型名称可以替换

#include <iostream>

using namespace std;

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

int main() {
    int a = 10;
    int b = 20;
    mySwap(a, b);
    cout << "a = " << a << endl;
    cout << "b = " << b << endl;

    string c = "111";
    string d = "222";
    mySwap<string&gt;(c, d);
    cout << "c = " << c << endl;
    cout << "d = " << d << endl;
    return 0;
}

使用模板有两种方式,一种是自动推导类型,一种是显示指定类型
这里使用模板,可以传入多种类型的参数,当int类型参数传入的时候,T被推导成int类型,当string类型参数传入的时候,T被推导成string类型。

2.2 函数模注意事项

数模定义于函数前,只能用于其后的第一函数,且函数模板必须推导出一致的类型

int a = 10;
char b = 'a';
mySwap(a, b);

编译失败,因为推导不出一致的类型

template<typename T&gt;
void func() {
    cout << "func" << endl;
}

int main() {
//    func();
    func<int&gt;();
}

必须推导出具体的类型,函数模板才能够正常调用,否则编译失败

2.3 普通函数和函数模板的区别

普通函数可以发生自动类型转换,函数模板如果使用自动类型推导,则不会发生类型的隐式转换。如果函数模板使用显示类型,则可以发生隐式转换

template<typename T&gt;
int addNum(T a, T b) {
    return a + b;
}

int main() {
    int a = 10;
    int b = 20;
    addNum(a, b);
    //addNum(a, c);
    char c = 'a';
    addNum<int&gt;(a, c);
}

2.4 普通函数与函数模板的调用规则

#include <iostream>

using namespace std;

void print(int a, int b) {
    cout << "普通函数" << endl;
}

template<typename T>
void print(T a, T b) {
    cout << "函数模板" << endl;
}

template<typename T>
void print(T a, T b, T c) {
    cout << "重载的函数模板" << endl;
}

int main() {
    int a = 10;
    int b = 20;
    // 调用普通函数
    print(a,b);

    // 调用函数模板
    print<>(a,b);

    // 调用重载的函数模板
    int c = 30;
    print(a, b, c);

    // 调用函数模板
    char d = 'a';
    char e = 'b';
    print(d, e);
}

实践中,如果提供了函数模板就没有必要提供普通函数了,以免造成二义性

2.5 模板的局限性

模板并不是完成的

template<typename T>
void f(T a, T b) {
	a = b;
}

对于上述的赋值操作,如果传入的参数数组,则无法实现

template<class T>
void f(T a, T b) {
	if (a > b) { ... }
}

如果传入的参数自定义的类对象,则上面的模板也无法运行
对于上面的这种问题,C++提供了一种函数模板的重载方式,可以为这些特定的类型提供具象化的模板。

#include <iostream>

using namespace std;

class Person {
public:
    Person(string name, int age) {
        this->m_Name = name;
        this->m_Age = age;
    }

    string m_Name;
    int m_Age;
};

template<class T>
bool myCompare(T &amp;a, T &amp;b) {
    if (a == b) {
        return true;
    } else {
        return false;
    }
}

// 提供具象化的模板函数重载
template<>
bool myCompare(Person &amp;p1, Person &amp;p2) {
    if (p1.m_Name == p2.m_Name && p1.m_Age == p2.m_Age) {
        return true;
    } else {
        return false;
    }
}

int main() {
    int a = 10;
    int b = 10;
    cout << myCompare(a, b) << endl;

    Person p1("zhangsan", 23);
    Person p2("lisi", 23);
    cout << myCompare(p1, p2) << endl;
    return 0;
}

使用具体化模板,可以解决定义类型的通用化

2 类模板

2.1 类模板语法

类模板的作用:创建一个通用的类,类中成员变量的类型可以不具体指定,用虚拟类型来代表
语法

template<typename T>

示例

#include <iostream>

using namespace std;

template<class NameType, class AgeType>
class Person {
public:
    Person(NameType name, AgeType age) {
        m_Name = name;
        m_Age = age;
    }

    void showPerson() {
        cout << "name = " << m_Name << " , age = " << m_Age << endl;
    }

    NameType m_Name;
    AgeType m_Age;
};

int main() {
    Person<string, int> person("zhangsan", 21);
    person.showPerson();
    return 0;
}

创建一个类模板,在创建对象的时候,可以通过显示的限定数据类型来让模板中的通用类型拥有具体的类型,也可以让编译器自动进行推导

Person person("zhangsan", 21);

2.2 类模板和函数模板的区别

类模板在模板参数定义的时候可以有默认值

#include <iostream>

using namespace std;

template<class NameType, class AgeType = int>
class Person {
public:
    Person(NameType name, AgeType age) {
        m_Name = name;
        m_Age = age;
    }

    void showPerson() {
        cout << "name = " << m_Name << " , age = " << m_Age << endl;
    }

    NameType m_Name;
    AgeType m_Age;
};

int main() {
    Person<string> person("zhangsan", 20);
    person.showPerson();
    return 0;
}

设置模板默认值,在使用模板类时,可以不需要限定默认参数的类型
对于默认参数类型,可以在显示限定的时候覆盖默认类型

Person<string, double> person("zhangsan", 22.3);

2.3 类模板中成员函数的创建时机

类模板中成员函数创建是在调用时才创建的

#include <iostream>

using namespace std;

class Person1 {
public:
    void showPerson1() {
        cout << "Person1 show" << endl;
    }
};

class Person2 {
public:
    void showPerson2() {
        cout << "Person2 show" << endl;
    }
};

template<class T>
class MyClass {
public:
    T obj;
    //类模板中的成员函数,并不是一开始就创建的,而是在模板调用时再生成
    void fun1() { obj.showPerson1(); }
    void fun2() { obj.showPerson2(); }
};

int main() {
    MyClass<Person1> m;
    m.fun1();
    // m.fun2();//编译会出错,说明函数调用才会去创建成员函数
    return 0;
}

当不调用fun2的时候,代码能够编译通过,调用的时候就会报错,因为调用的时候才会识别到T被初始化为Person1类型,而Person2中没有showPerson2函数。

2.4 类模板与继承

类模板与继承相关时,需要注意

#include <iostream>

using namespace std;

template<class T>
class Base
{
    T m;
};

// class Son:public Base  //错误,c++编译需要给子类分配内存,必须知道父类中T的类型才可以向下继承
class Son :public Base<int> //必须指定一个类型
{
};
void test01()
{
    Son c;
}

//类模板继承类模板 ,可以用T2指定父类中的T类型
template<class T1, class T2>
class Son2 :public Base<T2>
{
public:
    Son2()
    {
        cout << typeid(T1).name() << endl;
        cout << typeid(T2).name() << endl;
    }
};

void test02()
{
    Son2<int, char> child1;
}


int main() {
    test01();
    test02();
    return 0;
}

继承类模板,必须显示指定其类型,如果不显示指定,可以将需要指定父类的类型也声明成一个模板,在子类构造的时候传入。

2.5 类模板成员函数类外实现

#include <iostream>
#include <string>

using namespace std;

//类模板中成员函数类外实现
template<class T1, class T2>
class Person {
public:
    //成员函数类内声明
    Person(T1 name, T2 age);
    void showPerson();

public:
    T1 m_Name;
    T2 m_Age;
};

//构造函数 类外实现
template<class T1, class T2>
Person<T1, T2>::Person(T1 name, T2 age) {
    this->m_Name = name;
    this->m_Age = age;
}

//成员函数 类外实现
template<class T1, class T2>
void Person<T1, T2>::showPerson() {
    cout << "姓名: " << this->m_Name << " 年龄:" << this->m_Age << endl;
}

int main() {
    Person<string, int> p("Tom", 20);
    p.showPerson();
    return 0;
}

类模板成员函数的类外实现,除了需要重新声明模板外还需要限定他是一个类模板(使用<>)

2.6 类模板份文件编写

问题

推荐使用第二种方式

#ifndef STLDEMO1_PERSON_HPP
#define STLDEMO1_PERSON_HPP

#include<iostream>

using namespace std;

template<class T1, class T2>
class Person {
public:
    //成员函数类内声明
    Person(T1 name, T2 age);

    void showPerson();

public:
    T1 m_Name;
    T2 m_Age;
};

//构造函数 类外实现
template<class T1, class T2>
Person<T1, T2>::Person(T1 name, T2 age) {
    this->m_Name = name;
    this->m_Age = age;
}

//成员函数 类外实现
template<class T1, class T2>
void Person<T1, T2>::showPerson() {
    cout << "姓名: " << this->m_Name << " 年龄:" << this->m_Age << endl;
}

#endif

创建hpp文件,将声明和实现放到一个文件中进行导入。因为分文件导入,调用的时候才创建函数,最开始的时候只包含头文件没有生成对应的函数,在调用的时候才生成,所以找不到函数。

2.7 类模板与友元

可以分为两种,一种是全局函数类内实现,一种是全局函数类外实现
1、全局函数类内实现

#include <iostream>
#include <string>

using namespace std;

template<class T1, class T2>
class Person {
    //1、全局函数配合友元   类内实现
    friend void printPerson(Person<T1, T2> &p) {
        cout << "姓名: " << p.m_Name << " 年龄:" << p.m_Age << endl;
    }

public:
    Person(T1 name, T2 age) {
        this->m_Name = name;
        this->m_Age = age;
    }

private:
    T1 m_Name;
    T2 m_Age;
};

int main() {
    Person<string, int> p("Jerry", 30);
    printPerson(p);
    return 0;
}

直接类内实现,并调用
类外实现较为复杂一些

#include <iostream>
#include <string>

using namespace std;

template<class T1, class T2>
class Person;

template<class T1, class T2>
void printPerson(Person<T1, T2> &p) {
    cout << "姓名: " << p.m_Name << " 年龄:" << p.m_Age << endl;
}

template<class T1, class T2>
class Person {
    friend void printPerson<>(Person<T1, T2> &p);
public:
    Person(T1 name, T2 age) {
        this->m_Name = name;
        this->m_Age = age;
    }

private:
    T1 m_Name;
    T2 m_Age;
};

int main() {
    Person<string, int> p("Jerry", 30);
    printPerson(p);
    return 0;
}

友元定义的时候需要加<>让编译器识别友元函数是一个模板函数。否则编译失败,其他的调整就是让编译器能够识别对应函数或类,能够编译通过即可

原文地址:https://blog.csdn.net/weixin_49274713/article/details/134654244

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任

如若转载,请注明出处:http://www.7code.cn/show_17937.html

如若内容造成侵权/违法违规/事实不符,请联系代码007邮箱suwngjj01@126.com进行投诉反馈,一经查实,立即删除

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注