c++函数模板学习
创始人
2024-05-29 13:29:26
0

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

  1. 如果函数模板与普通函数都可以调用,优先调用普通函数
  2. 可以通过空模板参数列表,强制调用函数模板
  3. 函数模板可以发生函数重载
  4. 如果函数模板可以产生更好的匹配,优先调用函数模板

强制调用函数模板

void my_print(int a, int b)
{cout << "调用的普通函数" << endl;
}template
void my_print(T a, T b)
{cout << "调用的函数模板" << endl;
}void test02()
{int a = 10;int b = 20;// 这里通过空模板参数列表,强制调用函数模板my_print<>(a, b);
}

函数模板的局限性

例如:

template
void func(T a, T b)
{a = b;
}

在上述代码中提供的赋值操作,如果传入的a和b是一个数组,就无法实现了。
因为c++为了解决这种问题,提供模板的重载,可以为这些特定的类型提供具体化的模板

template
bool my_compare(T &a, T &b)
{if (a == b){return true;}else{return false;}
}// 利用具体化Person的版本实现代码,具体化优先调用
template<> bool my_compare(Person &a, Person &b)
{if (a.m_name == b.m_name && a.m_age == b.m_age){return true;}else{return false;}
}void test03()
{Person p1("Tom", 10);Person p2("Tom", 10);bool res = my_compare(p1, p2);cout << "res = " << res << endl;
}

类模板

类模板语法

类模板与函数模板区别

  1. 类模板没有自动类型推导的使用方式
  2. 类模板在模板参数列表中可以有默认参数
#include 
#include 
// 类模板
// 类模板在模板参数列表中可以有默认参数
template
class Person
{
public:Person(NameType name, AgeType age){m_name = name;m_age = age;}void show_info(){cout << "name: " << this->m_name << endl;cout << "age: " << this->m_age << endl;}NameType m_name;AgeType m_age;
};void test03()
{// 这种写法是错误的, 类模板无法自动类型推导// Person p1("张三", 23);Person p1("张三", 23);Person p2("李四", 24)p1.show_info();
}

类模板对象做函数参数

有三种传递方式

  1. 指定传入类型
  2. 参数模板化
  3. 整个类模板化

实际开发中,最常用的还是第一种

// 类模板
template
class Person
{
public:Person(NameType name, AgeType age){m_name = name;m_age = age;}void show_info(){cout << "name: " << this->m_name << endl;cout << "age: " << this->m_age << endl;}NameType m_name;AgeType m_age;
};// 1. 指定传入类型
void print_person(Person &p)
{p.show_info();
}// 2. 参数模板化
template
void print_person2(Person &p)
{p.show_info();
}// 3. 整个类模板化
template
void print_person3(T &p)
{p.show_info();cout << "T = " << typeid(T).name() << endl;
}void test03()
{Person p1("张三", 23);print_person(p1);Person p2("李四", 25);print_person2(p2);Person p3("王五", 18);print_person3(p3);
}

类模板中成员函数的类外实现

// 类内声明, 类外实现
template
class Person
{
public:Person(NameType name, AgeType age);void show_info();NameType m_name;AgeType m_age;
};template
Person::Person(NameType name, AgeType age)
{this->m_name = name;this->m_age = age;
}template
void Person::show_info()
{cout << "name: " << this->m_name << endl;cout << "age: " << this->m_age << endl;
}void test02()
{Person p1("张三", 23);p1.show_info();
}

类模板份文件编写

问题:
类模板中成员函数创建时机是在调用阶段,导致分文件编写时链接不到
解决:
解决方式1:直接包含.cpp源文件
解决方式2:将声明和实现写到同一个文件里,并更改后缀名为.hpp,hpp是约定的名称,不是强制
一般使用解决方式2
person.hpp中代码:

#pragma once
#include 
#include using namespace std;template
class Person
{
public:Person(NameType name, AgeType age);void show_info();NameType m_name;AgeType m_age;
};template
Person::Person(NameType name, AgeType age)
{this->m_name = name;this->m_age = age;
}template
void Person::show_info()
{cout << "name: " << this->m_name << endl;cout << "age: " << this->m_age << endl;
}

类模板与友元

建议:全局函数在类内实现

person.hpp代码:

#pragma once
#include 
#include using namespace std;template
class Person;template
void print_info2(Person p)
{cout << "类外实现-----" << p.m_name << " " << p.m_age << endl;
}template
class Person
{// 全局函数,类内实现friend void print_info(Person p){cout << "name: " << p.m_name << " " << "age: " << p.m_age << endl;}// 全局函数,类外实现// 加空模板参数列表, 表示这是一个函数模板的函数声明(这点很重要)// 如果全局函数是类外实现,需要让编译器提前知道这个函数的存在friend void print_info2<>(Person p);public:Person(NameType name, AgeType age);void show_info();private:NameType m_name;AgeType m_age;
};template
Person::Person(NameType name, AgeType age)
{this->m_name = name;this->m_age = age;
}template
void Person::show_info()
{cout << "name: " << this->m_name << endl;cout << "age: " << this->m_age << endl;
}

main.cpp代码:

void test02()
{Person p1("张三", 23);//    print_info2(p1);print_info(p1);
}

类模板案例

案例描述:

  • 可以对内置数据类型以及自定义数据类型的数据进行存储
  • 将数组中的数据存储到堆区(需要 new)
  • 构造函数中可以传入数组的容量
  • 提供对应的拷贝构造函数以及operator=防止浅拷贝问题
  • 提供尾插法和尾删法对数据中的数据操作
  • 可以通过下标的方式访问数组中元素
  • 可以获取数组中当前元素个数和数组容量

main.cpp中的代码:

#include 
#include #include "custom_array.hpp"using namespace std;class Person
{
public:// 空实现不写会报错Person() {}Person(string name, int age){this->m_name = name;this->m_age = age;}string m_name;int m_age;
};void print_arr(CustomArray& arr)
{for (int i = 0; i < arr.get_size(); i++){cout << arr[i] << "\t";}cout << endl;
}void print_person_arr(CustomArray& arr)
{for (int i = 0; i < arr.get_size(); i++){cout << "姓名:" << arr[i].m_name << "\t年龄:" << arr[i].m_age << endl;}
}void test01()
{CustomArray arr(10);//    // 拷贝构造调用
//    CustomArray arr2(arr);
//
//    CustomArray arr3(100);
//    arr3 = arr2;for (int i = 0; i < 5; i++){arr.push_back(i);}cout << "输出为:" << endl;print_arr(arr);cout << "arr的容量: " << arr.get_capacity() << endl;cout << "arr的大小: " << arr.get_size() << endl;CustomArray arr2(arr);arr2.pop_back();cout << "arr2输出为:" << endl;print_arr(arr2);cout << "arr2的容量: " << arr2.get_capacity() << endl;cout << "arr2的大小: " << arr2.get_size() << endl;
}void test02()
{CustomArray arr(10);Person p1("张三", 23);Person p2("李四", 35);Person p3("王五", 24);Person p4("赵六", 23);Person p5("钱七", 25);Person p6("孙八", 45);Person p7("周九", 30);arr.push_back(p1);arr.push_back(p2);arr.push_back(p3);arr.push_back(p4);arr.push_back(p4);arr.push_back(p6);arr.push_back(p7);print_person_arr(arr);
}int main() {
//    test01();test02();system("pause");return 0;
}

custom_array.hpp中的代码:

#pragma once
#include 
#include using namespace std;template
class CustomArray
{
public:// 有参构造CustomArray(int capacity){this->m_capacity = capacity;this->m_size = 0;this->p_address = new T[this->m_capacity];}// 拷贝构造CustomArray(const CustomArray& arr){this->m_capacity = arr.m_capacity;this->m_size = arr.m_size;// 浅拷贝会导致堆区数据重复释放// this->p_address = arr.p_address;// 所以要深拷贝this->p_address = new T[this->m_capacity];// 将arr中原先的数据都拷贝过来for(int i = 0; i < this->m_size; i++){this->p_address[i] = arr.p_address[i];}}// operator=CustomArray& operator=(const CustomArray& arr){// 先判断原来堆区是否有数据,如果有先释放if (this->p_address != nullptr){delete[] this->p_address;this->p_address = nullptr;this->m_capacity = 0;this->m_size = 0;}// 深拷贝this->m_capacity = arr.m_capacity;this->m_size = arr.m_size;this->p_address = new T[this->m_capacity];return *this;}// 尾插法void push_back(const T& val){// 判断容量是否已满if (this->m_capacity == this->m_size){cout << "容量已满,无法添加" << endl;return;}this->p_address[this->m_size] = val;    // 在数组末尾插入数据this->m_size++;}// 尾删法void pop_back(){// 让用户访问不到最后一个元素即可(逻辑上的删除)if (this->m_size == 0){return;}this->m_size--;}// 通过下标的方式访问数组(重载[])T& operator[](int idx){return this->p_address[idx];}// 返回数组的容量int get_capacity(){return this->m_capacity;}// 返回数组的大小int get_size(){return this->m_size;}~CustomArray(){if (this->p_address != nullptr){delete[] this->p_address;this->p_address = nullptr;}}private:T* p_address;       // 指向堆区开辟的真实数组int m_capacity;     // 数组容量int m_size;         // 数组大小
};

相关内容

热门资讯

监控摄像头接入GB28181平... 流程简介将监控摄像头的视频在网站和APP中直播,要解决的几个问题是:1&...
Windows10添加群晖磁盘... 在使用群晖NAS时,我们需要通过本地映射的方式把NAS映射成本地的一块磁盘使用。 通过...
protocol buffer... 目录 目录 什么是protocol buffer 1.protobuf 1.1安装  1.2使用...
在Word、WPS中插入AxM... 引言 我最近需要写一些文章,在排版时发现AxMath插入的公式竟然会导致行间距异常&#...
【PdgCntEditor】解... 一、问题背景 大部分的图书对应的PDF,目录中的页码并非PDF中直接索引的页码...
修复 爱普生 EPSON L4... L4151 L4153 L4156 L4158 L4163 L4165 L4166 L4168 L4...
Fluent中创建监测点 1 概述某些仿真问题,需要创建监测点,用于获取空间定点的数据࿰...
educoder数据结构与算法...                                                   ...
MySQL下载和安装(Wind... 前言:刚换了一台电脑,里面所有东西都需要重新配置,习惯了所...
MFC文件操作  MFC提供了一个文件操作的基类CFile,这个类提供了一个没有缓存的二进制格式的磁盘...