C++中有一个重要特性,那就是模板类型。类似于Objective-C中的泛型。C++通过类模板来实现泛型支持。

1 基础的类模板

类模板,可以定义相同的操作,拥有不同数据类型的成员属性。
通常使用template来声明。告诉编译器,碰到T不要报错,表示一种泛型.
如下,声明一个普通的类模板:

  1. template <typename T>
  2. class Complex{
  3.  
  4. public:
  5. //构造函数
  6. Complex(T a, T b)
  7. {
  8. this->a = a;
  9. this->b = b;
  10. }
  11.  
  12. //运算符重载
  13. Complex<T> operator+(Complex &c)
  14. {
  15. Complex<T> tmp(this->a+c.a, this->b+c.b);
  16. return tmp;
  17. }
  18.  
  19. private:
  20. T a;
  21. T b;
  22. }
  23.  
  24. int main()
  25. {
  26. //对象的定义,必须声明模板类型,因为要分配内容
  27. Complex<int> a(,);
  28. Complex<int> b(,);
  29. Complex<int> c = a + b;
  30.  
  31. return ;
  32. }

2 模板类的继承

在模板类的继承中,需要注意以下两点:

如果父类自定义了构造函数,记得子类要使用构造函数列表来初始化

  • 继承的时候,如果子类不是模板类,则必须指明当前的父类的类型,因为要分配内存空间
  • 继承的时候,如果子类是模板类,要么指定父类的类型,要么用子类的泛型来指定父类
  1. template <typename T>
  2. class Parent{
  3. public:
  4. Parent(T p)
  5. {
  6. this->p = p;
  7. }
  8.  
  9. private:
  10. T p;
  11. };
  12.  
  13. //如果子类不是模板类,需要指明父类的具体类型
  14. class ChildOne:public Parent<int>{
  15.  
  16. public:
  17. ChildOne(int a,int b):Parent(b)
  18. {
  19. this->cone = a;
  20. }
  21.  
  22. private:
  23. int cone;
  24. };
  25.  
  26. //如果子类是模板类,可以用子类的泛型来表示父类
  27. template <typename T>
  28. class ChildTwo:public Parent<T>{
  29.  
  30. public:
  31. ChildTwo(T a, T b):Parent<T>(b)
  32. {
  33. this->ctwo = a;
  34. }
  35.  
  36. private:
  37. T ctwo;
  38. };

3 内部声明定义普通模板函数和友元模板函数

普通模板函数和友元模板函数,声明和定义都写在类的内部,也不会有什么报错。正常。

  1. template <typename T>
  2. class Complex {
  3.  
  4. //友元函数实现运算符重载
  5. friend ostream& operator<<(ostream &out, Complex &c)
  6. {
  7. out<<c.a << " + " << c.b << "i";
  8. return out;
  9. }
  10.  
  11. public:
  12. Complex(T a, T b)
  13. {
  14. this->a = a;
  15. this->b = b;
  16. }
  17.  
  18. //运算符重载+
  19. Complex operator+(Complex &c)
  20. {
  21. Complex temp(this->a + c.a, this->b + c.b);
  22. return temp;
  23. }
  24.  
  25. //普通加法函数
  26. Complex myAdd(Complex &c1, Complex &c2)
  27. {
  28. Complex temp(c1.a + c2.a, c1.b + c2.b);
  29. return temp;
  30. }
  31.  
  32. private:
  33. T a;
  34. T b;
  35. };
  36.  
  37. int main()
  38. {
  39. Complex<int> c1(,);
  40. Complex<int> c2(,);
  41.  
  42. Complex<int> c = c1 + c2;
  43.  
  44. cout<<c<<endl;
  45.  
  46. return ;
  47. }

4 内部声明友元模板函数+外部定义友元模板函数

如果普通的模板函数声明在类的内部,定义在类的外部,不管是否处于同一个文件,就跟普通的函数一样,不会出现任何错误提示。但是如果是友元函数就会出现报错,是因为有二次编译这个机制存在。

4.1 模板类和模板函数的机制

在编译器进行编译的时候,编译器会产生类的模板函数的声明,当时实际确认类型后调用的时候,会根据调用的类型进行再次帮我们生成对应类型的函数声明和定义。我们称之为二次编译。同样,因为这个机制,会经常报错找不到类的函数的实现。在模板类的友元函数外部定义时,也会出现这个错误。解决方法是 “ 类的前置声明和函数的前置声明 ”。

按照普通模板函数的样式处理友元函数

  1. #include <iostream>
  2. using namespace std;
  3.  
  4. template <typename T>
  5. class Complex {
  6.  
  7. //友元函数实现运算符重载
  8. friend ostream& operator<<(ostream &out, Complex<T> &c);
  9.  
  10. public:
  11. Complex(T a, T b);
  12.  
  13. //运算符重载+
  14. Complex<T> operator+(Complex<T> &c);
  15.  
  16. //普通加法函数
  17. Complex<T> myAdd(Complex<T> &c1, Complex<T> &c2);
  18.  
  19. private:
  20. T a;
  21. T b;
  22. };
  23.  
  24. //友元函数的实现
  25. template <typename T>
  26. ostream& operator<<(ostream &out, Complex<T> &c)
  27. {
  28. out<<c.a << " + " << c.b << "i";
  29. return out;
  30. }
  31.  
  32. //函数的实现
  33. template <typename T>
  34. Complex<T>::Complex(T a, T b)
  35. {
  36. this->a = a;
  37. this->b = b;
  38. }
  39.  
  40. template <typename T>
  41. Complex<T> Complex<T>::operator+(Complex<T> &c)
  42. {
  43. Complex temp(this->a + c.a, this->b + c.b);
  44. return temp;
  45. }
  46.  
  47. template <typename T>
  48. Complex<T> Complex<T>::myAdd(Complex<T> &c1, Complex<T> &c2)
  49. {
  50. Complex temp(c1.a + c2.a, c1.b + c2.b);
  51. return temp;
  52. }
  53.  
  54. int main()
  55. {
  56. Complex<int> c1(,);
  57. Complex<int> c2(,);
  58.  
  59. Complex<int> c = c1 + c2;
  60.  
  61. cout<<c<<endl;
  62.  
  63. return ;
  64. }

友元函数的定义写在类的外部--错误信息

  1. Undefined symbols for architecture x86_64:
  2. "operator<<(std::__1::basic_ostream<char, std::__1::char_traits<char> >&, Complex<int>&)", referenced from:
  3. _main in demo1.o
  4. ld: symbol(s) not found for architecture x86_64
  5. clang: error: linker command failed with exit code 1 (use -v to see invocation)

上面的错误信息,就是典型的二次编译的错误信息,找不到友元函数的函数实现。所以,如果友元模板函数的定义写在函数的外部,需要进行类和函数的前置声明,来让编译器找到函数的实现

4.2 前置声明解决二次编译问题

类的前置声明
友元模板函数的前置声明
友元模板函数声明需要增加泛型支持

5 声明和定义分别在不同的文件(模板函数、模板友元)

类的声明和实现,分别在不同的文件下,需要增加一个hpp文件支持。或者尽量将模板函数与模板友元放在一个文件下。

类的声明与函数的声明写在.h文件
类的实现及函数的实现写在.cpp文件
将.cpp文件改成.hpp文件
在主函数中调用.hpp文件,而不是引用.h文件

如果碰到.h和.hpp文件都存在的情况下,引用.hpp文件。

demo2.h文件
存放类的声明和函数的声明

  1. #include <iostream>
  2. using namespace std;
  3.  
  4. //类的前置声明
  5. template <typename T>
  6. class Complex;
  7.  
  8. //友元函数的声明
  9. template <typename T>
  10. ostream& operator<<(ostream &out, Complex<T> &c);
  11.  
  12. template <typename T>
  13. class Complex {
  14.  
  15. //友元函数实现运算符重载
  16. friend ostream& operator<< <T> (ostream &out, Complex<T> &c);
  17.  
  18. public:
  19. Complex(T a, T b);
  20.  
  21. //运算符重载+
  22. Complex<T> operator+(Complex<T> &c);
  23.  
  24. //普通加法函数
  25. Complex<T> myAdd(Complex<T> &c1, Complex<T> &c2);
  26.  
  27. private:
  28. T a;
  29. T b;
  30. };

demo2.hpp文件
包括模板函数的实现

  1. #include "demo2.h"
  2.  
  3. //友元函数的实现
  4. template <typename T>
  5. ostream& operator<<(ostream &out, Complex<T> &c)
  6. {
  7. out<<c.a << " + " << c.b << "i";
  8. return out;
  9. }
  10.  
  11. //函数的实现
  12. template <typename T>
  13. Complex<T>::Complex(T a, T b)
  14. {
  15. this->a = a;
  16. this->b = b;
  17. }
  18.  
  19. template <typename T>
  20. Complex<T> Complex<T>::operator+(Complex<T> &c)
  21. {
  22. Complex temp(this->a + c.a, this->b + c.b);
  23. return temp;
  24. }
  25.  
  26. template <typename T>
  27. Complex<T> Complex<T>::myAdd(Complex<T> &c1, Complex<T> &c2)
  28. {
  29. Complex temp(c1.a + c2.a, c1.b + c2.b);
  30. return temp;
  31. }

main.cpp文件
需要调用hpp文件

  1. #include <iostream>
  2. using namespace std;
  3. #include "demo2.hpp"
  4.  
  5. int main()
  6. {
  7. Complex<int> c1(,);
  8. Complex<int> c2(,);
  9.  
  10. Complex<int> c = c1 + c2;
  11.  
  12. cout<<c<<endl;
  13.  
  14. return ;
  15. }

原文链接:https://www.jianshu.com/p/70ca94872418

【C++】C++中的类模板的更多相关文章

  1. 在VS中使用类模板出现出现LNK2019: 无法解析的外部符号错误。

    在VS中使用类模板出现出现LNK2019: 无法解析的外部符号错误,应在一个.h文件中完成方法的声明与实现,不要将实现放在cpp文件里,VS貌似不支持类模板分离

  2. C++中的类模板

    一.学习笔记 1.类模板的格式(1)声明 template<typename T> /* 使用T表示某种类型,比如: */ class AAA { private: T obj; publ ...

  3. c++ 中pair类模板的用法详解

    pair: 头文件:#include<utility> 类模板:template <class T1, class T2> struct pair 参数:T1是第一个值的数据类 ...

  4. VS中的类模板

    在目录C:\Program Files (x86)\Microsoft Visual Studio\2017\Community\Common7\IDE\ItemTemplates\CSharp\Co ...

  5. c++中的类(class)-----笔记(类模板)

    1,一个模板类至少具有一个类参数,类参数是个符号以表示将要被某个确定数据类型代替的类型. #include<iostream> #include<string> using n ...

  6. C++:类模板与模板类

    6.3 类模板和模板类 所谓类模板,实际上是建立一个通用类,其数据成员.成员函数的返回值类型和形参类型不具体指定,用一个虚拟的类型来代表.使用类模板定义对象时,系统会实参的类型来取代类模板中虚拟类型从 ...

  7. C++中的函数模板

    我们在定义函数时,可以通过定义函数模板,来简化一些功能相同而数据类型不同的函数的定义和调用过程. C++中的函数模板 对于类的声明来说,也有同样的问题.有时,有两个或多个类,其功能是相同的,仅仅是数据 ...

  8. c++的函数模板和类模板

    函数模板和普通函数区别结论: 函数模板不允许自动类型转化 普通函数能够进行自动类型转换 函数模板和普通函数在一起,调用规则: 1 函数模板可以像普通函数一样被重载 2 C++编译器优先考虑普通函数 3 ...

  9. C++_进阶之函数模板_类模板

     C++_进阶之函数模板_类模板 第一部分 前言 c++提供了函数模板(function template.)所谓函数模板,实际上是建立一个通用函数,其函数类型和形参类型不具体制定,用一个虚拟的类型来 ...

随机推荐

  1. MyBatis面试题整理

    MyBatis面试题整理 1.什么是MyBatis? 答:MyBatis是一个可以自定义SQL.存储过程和高级映射的持久层框架. 2.讲下MyBatis的缓存 答:MyBatis的缓存分为一级缓存和二 ...

  2. PostgreSQL分区表实现——pg_pathman分区表管理

    该博文用于自己学习记录,内容节选自: https://github.com/digoal/blog/blob/master/201610/20161024_01.md pg_pathman 创建分区表 ...

  3. SQL基础篇(MICK)

    SQL基础教程(Mick) 数据库和SQL C:\PostgreSQL\9.5\bin\psql.exe -U postgres -d shop 数据库的基本概念 数据库(DB):将大量数据保存起来, ...

  4. Java-驼峰命名与下划线命名互转

    package com.xsh.util; /** * String工具类 * * @author xieshuang * @date 2019-05-23 */ public class Strin ...

  5. Android开发学习了这些,上帝都淘汰不了你

    曾听过很多人说Android学习很简单,做个App就上手了,工作机会多,毕业后也比较容易找工作.这种观点可能是很多Android开发者最开始入行的原因之一. 在工作初期,工作主要是按照业务需求实现Ap ...

  6. FileReader 事件用法

    FileReader对象采用异步方式读取文件,在不同的读取阶段会触发不同的事件. 事件列表: (1).abort事件:读取中断或调用reader.abort()方法时触发. (2).error事件:读 ...

  7. 1062 Error 'Duplicate entry '1438019' for key 'PRIMARY'' on query

    mysql主从库同步错误:1062 Error 'Duplicate entry '1438019' for key 'PRIMARY'' on querymysql主从库在同步时会发生1062 La ...

  8. DT企业新闻也叫公司新闻简介调取方案

    今天我们讲的是企业新闻简介的事,由于destoon官方比较懒,企业新闻没有开发这个截字功能,我们就变通思维直接调取内容前100字,但是由于企业新闻是2个不同的 表,所以我们必须做点小事,  就是写点p ...

  9. 20180520模拟赛T1——math

    [问题描述] 小美有 n 个点 m 条边. 让你给每个点一个正整数编号. 每条边有两个属性,相连的两个点的编号的 GCD 和 LCM. 题目保证整张图连通. 让你构造出一个编号. [输入格式] 从文件 ...

  10. sed 用法

    sed 用法 sed的其他用法如下: 1.删除行首空格 sed 's/^[ ]*//g' filename sed 's/^ *//g' filename sed 's/^[[:space:]]*// ...