C++ DAY07 模板

简介

一种用于实现通用编程的机制。
通过使用模板,我们可以编写可复用的代码,可以适用于多种数据类型。
C++ 模板的语法使用角括号 (< >) 来表示泛型类型,并使用关键字 template 来定义和声明模板。
示例

cpp 复制代码
#include <iostream>
// 定义一个模板函数
template<typename T>
T add(T a, T b) {
    return a + b;
}
int main() {
    int x = 5, y = 10;
    float f1 = 2.5, f2 = 3.7;
// 调用模板函数
    std::cout << "Sum of integers: " << add(x, y) << std::endl;
    std::cout << "Sum of floats: " << add(f1, f2) << std::endl;
    return 0;
}

优点:

提高代码的灵活性和复用性

总结

将数据类型可以作为参数进行传递

原理
编译器会对函数模板进行两次编译,
在声明的地方对模板代码本身进行编译,
在调用的地方对参数替换后的 代码进行编译。

分类

函数模板

类模板

函数模板

步骤

1,声明
2,使用

声明语法

位置:在函数上

cpp 复制代码
//class 和 typename 都是一样的,用哪个都可以
template<class 模板名,class 模板名,...>
//或
template<typename 模板名,typename 模板名,...>

使用语法

//在该方法任何一处使用数据类型的地方都可以使用模板名替换

注意

1,函数模板可以自动推导参数的类型 , 但是不会进行类型转换
2,函数模板可以自动类型推导 , 也可以显式指定类型
3,只能在声明的所在函数中使用

示例

cpp 复制代码
//定义模板
template<class 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. 函数模板显式指定类型
    MySwap<char>(c1, c2);
    cout << "c1:" << c1 << " c2:" << c2 << endl;
}

补充

函数模板会编译两次:
1,在加载时对函数模板进行第一次编译
2,在调用时推导 T 的类型再次将函数模板编译为模板函数

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

1, 函数模板不允许自动类型转化 普通函数能够自动进行类型转。 ( 示例 1)

cpp 复制代码
template<typename T>
void func(T a, T b){}
//普通函数
void func02(int a, int b){}
void test02()
{
func(10, 20);
//func(10, 'a');//err
func02(10, 'a');
}

2, 函数模板和普通函数同时识别 , 优先使用普通函数 , 加 <> 强制使用函数模板

cpp 复制代码
template<typename T>
void func(T a, T b){
cout<<"函数模板"<<endl;
}
//普通函数
void func(int a, int b){
cout<<"普通函数"<<endl;
}
void test02()
{
//普通函数
func(10, 20);
//显示调用函数模板
func<>(10, 20);
}

3, 函数模板可以使用 <>, 普通函数不行

函数模板的局限性

示例

cpp 复制代码
template <class Z>
void print(Z z)
{
    cout << z << endl;
}
class Person{
private:
    char name[32];
    int age;
public:
Person()
{
}
Person(char name[],int age)
{
    strcpy(this -> name,name);
    this -> age = age;
}
~Person()
{
}
};
int main(int argc, char *argv[])
{
    Person p("张三",18);
//此时函数模板已经推导出类型为Person,但是因为Person没有对<<运算符进行重
载,所以依据无法使用
    print(p);
    return 0;
}

解决方案 1 ,重载 << 函数

cpp 复制代码
template <class Z>
void print(Z z)
{
    cout << z << endl;
}
class Person{
friend ostream& operator <<(ostream& out,Person& p);
private:
    char name[32];
    int age;
public:
    Person()
    {
    }
    Person(char name[],int age)
    {
        strcpy(this -> name,name);
        this -> age = age;
    }
    ~Person()
    {
    }
};
ostream& operator <<(ostream& out,Person& p)
{
    out << p.name << " " << p.age << endl;
    return out;
}
int main(int argc, char *argv[])
{
    Person p("张三",18);
    print(p);
    return 0;
}

解决方案 2 :函数模板具体化

cpp 复制代码
template <class Z>
void print(Z z)
{
    cout << z << endl;
}
class Person{
friend void print<Person>(Person p);
private:
    char name[32];
    int age;
public:
    Person()
    {
    }
    Person(char name[],int age)
    {
        strcpy(this -> name,name);
        this -> age = age;
    }
    ~Person()
    {
    }
};
//函数模板具体化
template<> void print<Person>(Person p)
{
    cout << p.name << " " << p.age << endl;
}
int main(int argc, char *argv[])
{
    Person p("张三",18);
    print(p);
    print(10);
    return 0;
}

类模板

步骤

定义

使用

定义

语法

cpp 复制代码
template <class 模板名1,class 模板名2>

位置:类的上面

使用

创建对象

语法
类名 < 实际数据类型 1, 实际数据类型 2,...> 对象名 ( 实参列表 );
注意
类模板示例化对象是不能自动推导类型 , 需指定
示例

cpp 复制代码
#include <iostream>
#include <cstring>
using namespace std;
//定义模板
template <class T,class Z>
class Data{
private:
    T t1;
    Z z1;
public:
    Data(){}
    Data(T t,Z z)
    {
        t1 = t;
        z1 = z;
    }
    void showData()
    {
        cout << t1 << " " << z1 << endl;
    }
};
void test01(){
//创建模板类对象
//Data d01;//类模板示例化对象是不能自动推导类型,错误的
//Data d02(10,'a');//类模板示例化对象是不能自动推导类型,错误的
    Data<int,char> d03(10,'a');//类模板示例化对象时必须指定模板对应的数据
类型
    Data<string,float> d04("abc",18.8f);
    d03.showData();
    d04.showData();
}
int main(int argc, char *argv[])
{
    test01();
    return 0;
}

类模板成员函数外部实现

注意 : 外部实现函数时也需定义与类相同的函数模板
示例

cpp 复制代码
#include <iostream>
#include <cstring>
using namespace std;
//定义模板
template <class T,class Z>
class Data{
private:
    T t1;
    Z z1;
public:
    Data(){}
    Data(T t,Z z);
    void showData();
};
template<class T, class Z>
Data<T,Z>::Data(T t, Z z)
{
    t1 = t;
    z1 = z;
}
template<class T, class Z>
void Data<T,Z>::showData()
{
    cout << t1 << " " << z1 << endl;
}
void test01(){
    Data<string,float> d01("abc",18.8f);
    d01.showData();
}
int main(int argc, char *argv[])
{
    test01();
    return 0;
}

类模板的分文件使用

注意:
在实例化模板之前,编译器对模板的定义体是不处理的
在实例化模板时编译器必须在上下文中可以查看到其定义实体
因此模板的实例化与定义体必须放到同一文件中。
但是如果文件名为.h 皆为是头文件 , 在头文件中定义函数不符合头文件的将声明与实例分开的规则
顾将头文件名改为hpp, 并在前内部进行示例化
示例
data.hpp

cpp 复制代码
#ifndef DATA_H
#define DATA_H
#include<iostream>
using namespace std;
template<class T1, class T2>
class Data
{
private:
    T1 a;
    T2 b;
public:
    Data();
    Data(T1 a, T2 b);
    void showData();
};
template<class T1, class T2>
Data<T1,T2>::Data()
{
}
template<class T1, class T2>
Data<T1,T2>::Data(T1 a, T2 b)
{
    this->a = a;
    this->b = b;
}
template<class T1, class T2>
void Data<T1,T2>::showData()
{
    cout<<"a="<<a<<", b="<<b<<endl;
}
#endif // DATA_H

main.cpp

cpp 复制代码
#include <iostream>
#include "data.hpp"
using namespace std;
int main(int argc, char *argv[])
{
    Data<int,char> ob1(100, 'a');
    ob1.showData();
    return 0;
}

类模板作为函数参数

注意 : 该函数必须是类模板的友元函数
示例

cpp 复制代码
#include <iostream>
using namespace std;
template <class X>
class A{
friend void printA(A<X>& a);
template <class T>
friend void printB(A<T>& a);
private:
    X x;
public:
    A(){}
    A(X x)
    {
        this -> x = x;
    }
    void showA()
    {
        cout << x << endl;
    }
};
//普通函数作为类模板的友缘函数
void printA(A<int> &a)
{
    cout << a.x << endl;
}
void test01()
{
    A<int> a(10);
    printA(a);
}
//模板函数作为类模板的友缘函数
template <class T>
void printB(A<T> &a)
{
    cout << a.x << endl;
}
void test02()
{
    A<int> a(10);
    printB(a);
}
int main(int argc, char *argv[])
{
    test02();
    return 0;
}

类模板的派生

1, 类模板派生普通子类
示例

cpp 复制代码
#include <iostream>
using namespace std;
template <class T>
class Fu{
private:
    T t;
public:
    Fu()
    {
    }
    Fu(T t)
    {
        this -> t = t;
    }
    void showFu()
    {
        cout << t << endl;
    }
};
//子类实例父类模板数据
class Zi01:public Fu<int>
{
public:
    Zi01()
    {
    }
    Zi01(int x):Fu(x)
    {
    }
};
int main(int argc, char *argv[])
{
    Zi01 z01(10);
    z01.showFu();
    return 0;
}

2, 类模板派生类模板
示例

cpp 复制代码
#include <iostream>
using namespace std;
template <class T>
class Fu{
private:
    T t;
public:
    Fu()
    {
    }
    Fu(T t)
    {
        this -> t = t;
    }
    void showFu()
    {
        cout << t << endl;
    }
};
template <class X>
class Zi02:public Fu<X>{
public:
    Zi02()
    {
    }
    Zi02(X x):Fu<X>(x)
    {
    }
};
int main(int argc, char *argv[])
{
    Zi02<char> z02('a');
    return 0;
}

练习自定义集合

MyArr.hpp

cpp 复制代码
#ifndef MYARR_H
#define MYARR_H
#include <iostream>
using namespace std;
template <class T>
class MyArr{
private:
    T* ts;
    int size;//数据长度
    int count;//容量
public:
    MyArr();
    MyArr(const MyArr& myarr);
    ~MyArr();
    T get(int index);
    bool add(T t);
    int getSize();
};
template<class T>
MyArr<T>::MyArr()
{
    size = 0;
    count = 5;
    ts = new T[count];
}
template <class T>
MyArr<T>::MyArr(const MyArr<T>& myarr)
{
    this->ts = new T[myarr.count];
    this->size = myarr.size;
    this->count= myarr.count;
    /**
    * C库函数void *memcpy(void *str1, const void *str2, size_t n)
    * 从存储区 str2 复制 n 个字节到存储区 str1。
    */
    memcpy(ts,myarr.ts,this->size*sizeof(T));
}
template <typename T>
MyArr<T>::~MyArr()
{
    if(ts == NULL)
    {
    return;
    }
    delete[] ts;
    ts = NULL;
}
template <typename T>
T MyArr<T>::get(int index)
{
    if(index < 0 || index > size)
    {
        return NULL;
    }
        return ts[index];
}
template <class T>
bool MyArr<T>::add(T t)
{
    if(size == count)
    {
        T * newTs = new T[count * 2];
        memcpy(newTs,ts,sizeof(T)*size);
        delete[] ts;
        ts = newTs;
        count *= 2;
    }
    ts[size] = t;
    size++;
    return true;
}
template<class T>
int MyArr<T>::getSize()
{
    return size;
}
#endif // MYARR_H

main.cpp

cpp 复制代码
#include <iostream>
#include "myarr.hpp"
#include "cstring"
using namespace std;
int main(int argc, char *argv[])
{
    MyArr<int> arr01;
    arr01.add(1);
    arr01.add(2);
    arr01.add(3);
    arr01.add(4);
    arr01.add(5);
    arr01.add(6);
    int s01 = arr01.getSize();
    for(int i = 0; i < s01; i++)
    {
        cout << arr01.get(i) << endl;
    }
    MyArr<int> arr02;
    arr02.add('a');
    arr02.add('b');
    arr02.add('c');
    arr02.add('d');
    arr02.add('e');
    arr02.add('f');
    int s02 = arr02.getSize();
    for(int i = 0; i < s02; i++)
    {
        cout << (char)arr02.get(i) << endl;
    }
    MyArr<string> arr03;
    arr03.add("abc");
    arr03.add("123");
    arr03.add("qaz");
    arr03.add("hello");
    arr03.add("hi");
    int s03 = arr03.getSize();
    for(int i = 0; i < s03; i++)
    {
        cout << arr03.get(i) << endl;
    }
    return 0;
}

类型转换

上行转换

概念 : 父类指针指向子类空间
注意
安全的

下行转换

子类指针指向父类空间
注意
不安全

类型转换

静态转换

语法

static_cast<T>(要转换的数据)

示例
// 基本类型转换 支持
int num = static_cast < int > ( 3.14f );
// 基本指针类型转换 不支持
float f = 0.0f ;
//int *p1 = static_cast<int *>(&f);
// 上行转换 支持(安全)
Base * p2 = static_cast < Base *> ( new Son );
// 下行转换 支持(不安全)
Son * p3 = static_cast < Son *> ( new Base );
// 不相关类型转换 不支持
//Son *p4 = static_cast<Son *>(new Other);

动态转换

语法
dynamic_cast<T>(要转换的数据 )
示例
// 基本类型转换 不支持
//int num = dynamic_cast<int>(3.14f);
// 基本指针类型转换 不支持
float f = 0.0f ;
//int *p1 = dynamic_cast<int *>(&f);
// 上行转换 支持(安全)
Base * p2 = dynamic_cast < Base *> ( new Son );
// 下行转换 不支持(不安全)
//Son *p3 = dynamic_cast<Son *>(new Base);
// 不相关类型转换 不支持
//Son *p4 = dynamic_cast<Son *>(new Other);
常量转换
语法
const_cast<T>
注意
只能对指针与引用的变量使用
示例

cpp 复制代码
//将非const 转换成 const
int num = 10;
const int *p1 = const_cast<const int *>(&num);
//将const 转换成 非const
const int data=0;
int *p = const_cast<int *>(&data);

5, 重新解释转换(垃圾一个不用看)

相关推荐
Swift社区2 小时前
在 Swift 中实现字符串分割问题:以字典中的单词构造句子
开发语言·ios·swift
没头脑的ht2 小时前
Swift内存访问冲突
开发语言·ios·swift
没头脑的ht2 小时前
Swift闭包的本质
开发语言·ios·swift
wjs20242 小时前
Swift 数组
开发语言
南东山人2 小时前
一文说清:C和C++混合编程
c语言·c++
stm 学习ing3 小时前
FPGA 第十讲 避免latch的产生
c语言·开发语言·单片机·嵌入式硬件·fpga开发·fpga
湫ccc4 小时前
《Python基础》之字符串格式化输出
开发语言·python
mqiqe4 小时前
Python MySQL通过Binlog 获取变更记录 恢复数据
开发语言·python·mysql
AttackingLin4 小时前
2024强网杯--babyheap house of apple2解法
linux·开发语言·python