✨引言

书接上文,今天来学习C++模板知识中的第二大模块,也就是类模板的使用。

《C++提高编程专栏主要针对C++泛型编程STL技术做详细讲解,深入研究C++的使用,对C/C++感兴趣的小伙伴可以订阅专栏共同学习,专栏还在持续更新中


✨目录

类模板

类模板与函数模板的区别

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

类模板对象做函数参数

类模板与继承

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

类模板的分文件编写

类模板和友元

✨总结


类模板

建立一个通用类,类中的成员数据类型可以不具体确定,先用一个虚拟的类型来代表

语法:
template<typename T>
创建类

示例:

template<class T1,class T2>
class Person
{
public:T1 name;T2 age;
};

先使用template关键字声明类模板,接着创建Person类,属性name和age用虚拟类型T1和T2声明,这就是最简单的类模板定义。

类模板与函数模板的区别

区别一:类模板没有自动类型推导方式
区别二:类模板在模板参数列表中可以有默认参数
注意事项:有默认参数的条件是参数列表的末尾需要指定数据类型;
由于没有自动类型推导方式,所以使用的时候不能省略<>

示例:

template<class T1, class T2=int>
class Person
{
public:T1 name;T2 age;Person(T1 name, T2 age){this->age = age;this->name = name;}void showInfo(){cout << "姓名:" << name << " 年龄:" << age << endl;}
};
void test02()
{//Person p("叶落秋白", 18);报错,缺少对应的参数列表Person<string,int>p1("叶落秋白", 18);Person<string>p2("叶落秋白", 18);p.showInfo();
}

可以看到第一行代码T2的后面指定了类型为整型,所以创建类模板对象的时候可以省略T2数据类型的指定,但是切记 :在不给参数列表最后一个虚拟类型指定数据类型的情况下是无法做到参数列表含有默认参数的。

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

 类模板中的成员函数并不是一开始就创建的,只有在调用的时候才会被调用。这个知识点在下面的内容的理解上占据着重要作用。

代码测试:

class A
{
public:void showInfo(){cout << "调用A类的成员函数" << endl;}
};
class B
{
public:void showInfo(){cout << "调用B类的成员函数" << endl;}
};
template<class T>
class MyStudy
{
public:T pre;//类模板中的成员函数void show(){pre.showInfo();}
};
void test03()
{MyStudy<A>* S = new MyStudy<A>();//创建A类属性的类模板对象SMyStudy<B>* D = new MyStudy<B>();//创建B类属性的类模板对象DS->show();D->show();delete S;//释放堆区指针delete D;S = NULL;D = NULL;
}

由于成员属性pre的类型未指定是A类还是B类,所以编译器并不会创建成员函数,无法识别showInfo函数是哪一个类的成员函数。只有我们创建的时候给类模板对象指针指定T类型,这时候调用show方法编译器才会创建成员函数,实现对应的功能。

运行效果:

类模板对象做函数参数

三个形式:

指定传入类型

参数模板化

整个类模板化

代码演示:

template<class T1,class T2=int>
class Per
{
public:T1 name;T2 height;Per(T1 name, T2 height){this->name = name;this->height = height;}void showInfo(){cout << "姓名:" << name << " 身高:" << height << endl;}
};
//1、指定传入类型
void printInfo(Per<string, int>& p)
{p.showInfo();
}//2、参数模板化
template<class T1,class T2>
void printInfo1(Per<T1, T2>& p)
{p.showInfo();cout << "T1的类型为:" << typeid(T1).name() << endl;cout << "T2的类型为:" << typeid(T2).name() << endl;
}
//3、整个类模板化
template<class t>
void printInfo2(t & p)
{p.showInfo();cout << "t的类型为:" << typeid(t).name() << endl;
}
void test04()
{Per<string> p("叶落秋白", 183);printInfo(p);
}
void test4()
{Per<string> p("微凉秋意", 184);printInfo1(p);
}
void test4a()
{Per<string>p("落叶归根",185);printInfo2(p);
}

    小结:第一种指定传入类型的方式最为常用,查看自动推导类型可以调用typeid(虚拟类型).name()函数。其实string类型原名特别长,调用typeid函数的时候可以查看,另外也可以直接查看整个类模板化时虚拟类型t的数据类型。

运行效果:

类模板与继承

        类模板的继承不同于普通类的继承。这是因为类模板的属性数据类型事先并未指定,所以子类无法分配内存空间。

要点:

直接继承会提示缺少基类类的参数列表,需要继承的时候在基类后面指定<数据类型>
如果想要灵活指定父类中的T数据类型,子类也要变成类模板

代码示例:

template<class T>
class Base
{
public:Base(){cout << "此时父类T的数据类型是:" << typeid(T).name() << endl;}T m;
};
//class Son :public Base<int>//这里可以直接指定,不过为了灵活指定,将子类变为类模板
//子类变成类模板
template<class T1,class T2>
class Son :public Base<T2>
{
public:T1 n;Son() {cout << "T1的数据类型为:" << typeid(T1).name()<< endl;cout << "T2的数据类型为:" << typeid(T2).name()<< endl;}
};
void test05()
{Son<int,char> s;
}

  将子类变为类模板的时候可以自由添加子类的属性,这里子类Son的属性是T1类型的n,当然继承的T2属性的m也存在。在继承的时候将T2作为父类的虚拟类型,那么创建子类对象的时候指定的数据类型char就会先传给T2,T2再传给父类的T,那么就完成了灵活指定继承数据类型的工作

        创建子类对象是会自动调用父类构造,那么就能调用事先设置好的typeid函数来查看由子类指定的父类的虚拟数据类型的具体类型。

运行效果:

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

        成员函数类外实现需要在加作用域的前提下再加类模板的参数列表

代码演示:

template<class T1, class T2>
class Person
{
public:T1 name;T2 height;Person(T1 name, T2 height);//构造函数声明void showInfo();//showInfo函数声明
};
//构造函数类外实现
template<class T1,class T2>
Person<T1,T2>::Person(T1 name, T2 height)
{this->height = height;this->name = name;
}
template<class t1, class t2>
void Person<t1,t2>::showInfo()
{cout << "姓名:" << name << " 身高:" << height << endl;
}
//成员函数类外实现
void test06()
{Person<string, int> p("叶落秋白",184);p.showInfo();
}

 虽然在加作用域的前提下加了类模板的参数列表,但是编译器并不认识那些虚拟类型,所以还需要在其上面紧跟着加上template关键字来声明虚拟类型。

类模板的分文件编写

问题所在:
由于类模板成员函数在调用时才创建,只包含头文件,再函数调用时会出现无法解析的命令
解决方案:
 1、包含源文件(.cpp)不常用
 2、常将头文件和源文件写进一个文件里,文件后缀名改为.hpp,约定的格式

示例:

//person.hpp文件内容
#pragma once
#include<iostream>
using namespace std;
template<class T1, class T2>
class Person
{
public:T1 name;T2 height;Person(T1 name, T2 height);void showInfo();
};
//构造函数类外实现
template<class T1, class T2>
Person<T1, T2>::Person(T1 name, T2 height)
{this->height = height;this->name = name;
}
template<class t1, class t2>
void Person<t1, t2>::showInfo()
{cout << "姓名:" << name << " 身高:" << height << endl;
}
//调用的文件内容
#include"person.hpp"
void test07()
{Person<string, int> p("叶落秋白", 184);p.showInfo();
}

  将头文件声明和源文件实现全部写在一个头文件里并将头文件名改为.hpp就能解决类模板的分文件编写出现的问题了。

类模板和友元

特点:

全局函数类内实现,直接在类内声明友元即可
全局函数类外实现,需要告诉编译器类以及友元函数的实现,写在最上方

代码讲解:

//全局函数设置在类外时需要做的工作
template<class T1, class T2>
class Student;
template<class T1, class T2>
void showInfo1(Student<T1, T2>p)
{cout << "姓名:" << p.name << " 年龄:" << p.age << endl;
}
//类模板Student
template<class T1, class T2>
class Student
{//1、全局函数设置在类内friend void showInfo(Student p){cout << "姓名:" << p.name << " 年龄:" << p.age << endl;}//2、全局函数设置在类外friend void showInfo1<>(Student s);
private:T1 name;T2 age;
public:Student(T1 name, T2 age){this->age = age;this->name = name;}
};
void test08()
{Student<string, int> s1("叶落秋白", 18);Student<string, int> s2("微凉秋意", 20);showInfo(s1);showInfo1(s2);
}

首先我创建类模板Student将name 和 age 属性进行封装,我们知道通过friend可以将函数设置为友元函数,即可以访问类的私有属性。全局函数内部实现比较简单,直接在函数前面加上friend关键字即可。

全局函数的外部实现稍微复杂点:

首先将函数的声明写在类上方并在前面加上friend关键字,然后在类外完成该友元函数的实现。要特别注意:类外实现的时候需要加上template关键字声明虚拟类型,那么此时声明的部分也要加上空列表才行,要不然声明和实现不对应;而且类模板函数只有在调用时才会创建,所以需要把具体实现写在类上方,这样编译器才会创建这个函数;那么当你把实现写在最上面时,参数列表需要指定类型,那么就需要再把类模板的声明写在该实现上方。只有完成这些工作之后,才可以实现友元函数的类外实现。

运行效果:

总结

关于类的内容还是挺多的,类变成模板之后形式上也出现了或多或少的变化。想熟练掌握唯一的方法就是多记多练了,建议收藏反复观看。

       

全面总结C++类模板使用的基础知识相关推荐

  1. C++基础——关于模板的技巧性基础知识(typename、成员模板、模板的模板参数)

    typename template 成员模板 模板的模板 模板的模板 的实参匹配 本文继续深入探讨模板的基础知识,covers 内容如下: 关键字typename的另一种用法 将成员函数和嵌套类也定义 ...

  2. 模板引擎Freemarker基础知识

    Freemarker基础知识 Freemarker是什么 FreeMarker 基础指令 List指令 遍历Map数据 if指令 其它指令 运算符 空值处理 内建函数 入门Demo 要导入的依赖 配置 ...

  3. C++模板的一些基础知识

    一.函数模板 可看出就是将函数返回类型和形参类型去掉. 1.代码1 #include <iostream> #include <vector> #include <str ...

  4. 1023day5:class类属性方法、每次执行类属性+1、内建模块、时间装饰器wrapper、面向对象__slots__方法:限制类的属性等基础知识、正则表达式基础知识、多态鸭子类型

    文章目录 一.类class 1.Python类class 属性 方法 2.类的构造方法__init__() 3.每次执行一次类的属性+1 二.模块 1.内建模块 2.第三方模块 3.定义自己的模块 三 ...

  5. 计算机word基础知识菜单,Word试卷模板_电脑基础知识_IT/计算机_资料

    C. "格式"菜单中的"制表位"命令D. "格式"菜单中的"字体"命令 )计算机基础一.填空题(每空 1 分,共 20 ...

  6. 6.java中什么是类_类、对象(java基础知识六)

    1.Java约定俗成 java约定俗成1,类名接口名 一个单词首字母大写,多个单词每个单词首字母都大写2,方法名和变量名 一个单词全部小写,多个单词从第二个单词首字母大写 建议:如果能用英语尽量用英语 ...

  7. 二叉树应用-Huffman树类模板的实现(数据结构基础 第6周)

    简单实现来了Huffman树,在找最小堆的过程中使用到了自己做的最小堆MinHeap. 之前写的关于树的类模板中一直存在一个问题:结点的内存管理太混乱,有些甚至存在临时变量里.这里直接在类内直接定义了 ...

  8. C++ 泛型编程(一):模板基础:函数模板,类模板,模板原理,模板匹配规则

    类模板 函数模板 泛型编程 泛型编程,泛型即是指具有在多种数据类型上皆可操作的含义,其实就是能够帮助开发者编写完全一般化并可重复使用的算法,同样的工作不需要做多次,同样的算法针对不同的类型也不应该写多 ...

  9. 【C++基础】 类模板

    类模板 模板是将类中某些类型变为泛型,从而定义一个模板. 如下: 类模板的语法 直接进行对比: 泛型化之前 泛型化之后 类模板的实例化 注意:只要是对类模版进行实例化,编译器就会生成一个类!!! 显式 ...

最新文章

  1. python做电脑软件-程序员带你十天快速入门Python,玩转电脑软件开发(二)
  2. python3.5安装教程-Python 3.5安装教程
  3. MyBatis 实际使用案例-settings
  4. python测试程序的qps和响应时间代码_python性能测试,请求QPS测试
  5. mysql存储过程返回多个值_数据库mysql存储过程之返回多个值的方法示例
  6. 开发人员怎么看实施人员
  7. 【POJ - 3250 】Bad Hair Day (单调栈)
  8. 天池 在线编程 寻找比周围都大的点(模拟)
  9. charles-无法抓取https包的解决办法及效果
  10. 关于(我们流量表优化),分区表数据块过多,聚合又导致数据倾斜问题
  11. vue实现倒计时验证码
  12. 2018数据库流行度12月排行:Oracle续跌至年内低位,PostgreSQL激增创新高
  13. Navicat Premium 12.0.29 / 12.1.5.0注册机激活
  14. 看书必备:40个全球免费开放电子图书馆
  15. 服务器装系统鼠标键盘不能动,装系统鼠标键盘不能动
  16. linux 2.6.32文件系统的dentry父子关系
  17. erp系统与mes集成:制造业信息化发展中必不可少的阶段
  18. 提高你修养的100句话
  19. Retrofit自定义CallAdapterFactory
  20. 凌恩生物明星产品:一文读懂细胞器基因组!

热门文章

  1. AI模型神预测谁是卡塔尔世界杯冠军
  2. huipengly的2018年度总结
  3. Firefox火狐下载的文件名含空格时,无法显示完整文件名
  4. 用python编程小程序制作_Python编写四人24小时值班表生成小程序【简单粗暴】
  5. 《预训练周刊》第55期:中文预训练进展、大模型泛化、模型下游性能预测
  6. 数据库约束性条件作业笔记
  7. 超神学院的宇宙天体计算机,正文 第一章:银河之力被一分为二
  8. 转载:毕业半年在富士康的经历
  9. Python 3 字符串 format( ) 方法
  10. selenium.common.exceptions.WebDriverException: Message: unable to set