【C++】C++中的类模板
C++中有一个重要特性,那就是模板类型。类似于Objective-C中的泛型。C++通过类模板来实现泛型支持。
1 基础的类模板
类模板,可以定义相同的操作,拥有不同数据类型的成员属性。
通常使用template来声明。告诉编译器,碰到T不要报错,表示一种泛型.
如下,声明一个普通的类模板:
template <typename T> class Complex{ public: //构造函数 Complex(T a, T b) { this->a = a; this->b = b; } //运算符重载 Complex<T> operator+(Complex &c) { Complex<T> tmp(this->a+c.a, this->b+c.b); return tmp; } private: T a; T b; } int main() { //对象的定义,必须声明模板类型,因为要分配内容 Complex<int> a(10,20); Complex<int> b(20,30); Complex<int> c = a + b; return 0; }
2 模板类的继承
在模板类的继承中,需要注意以下两点:
如果父类自定义了构造函数,记得子类要使用构造函数列表来初始化
- 继承的时候,如果子类不是模板类,则必须指明当前的父类的类型,因为要分配内存空间
- 继承的时候,如果子类是模板类,要么指定父类的类型,要么用子类的泛型来指定父类
template <typename T> class Parent{ public: Parent(T p) { this->p = p; } private: T p; }; //如果子类不是模板类,需要指明父类的具体类型 class ChildOne:public Parent<int>{ public: ChildOne(int a,int b):Parent(b) { this->cone = a; } private: int cone; }; //如果子类是模板类,可以用子类的泛型来表示父类 template <typename T> class ChildTwo:public Parent<T>{ public: ChildTwo(T a, T b):Parent<T>(b) { this->ctwo = a; } private: T ctwo; };
3 内部声明定义普通模板函数和友元模板函数
普通模板函数和友元模板函数,声明和定义都写在类的内部,也不会有什么报错。正常。
template <typename T> class Complex { //友元函数实现运算符重载 friend ostream& operator<<(ostream &out, Complex &c) { out<<c.a << " + " << c.b << "i"; return out; } public: Complex(T a, T b) { this->a = a; this->b = b; } //运算符重载+ Complex operator+(Complex &c) { Complex temp(this->a + c.a, this->b + c.b); return temp; } //普通加法函数 Complex myAdd(Complex &c1, Complex &c2) { Complex temp(c1.a + c2.a, c1.b + c2.b); return temp; } private: T a; T b; }; int main() { Complex<int> c1(1,2); Complex<int> c2(3,4); Complex<int> c = c1 + c2; cout<<c<<endl; return 0; }
4 内部声明友元模板函数+外部定义友元模板函数
如果普通的模板函数声明在类的内部,定义在类的外部,不管是否处于同一个文件,就跟普通的函数一样,不会出现任何错误提示。但是如果是友元函数就会出现报错,是因为有二次编译这个机制存在。
4.1 模板类和模板函数的机制
在编译器进行编译的时候,编译器会产生类的模板函数的声明,当时实际确认类型后调用的时候,会根据调用的类型进行再次帮我们生成对应类型的函数声明和定义。我们称之为二次编译。同样,因为这个机制,会经常报错找不到类的函数的实现。在模板类的友元函数外部定义时,也会出现这个错误。解决方法是 “ 类的前置声明和函数的前置声明 ”。
按照普通模板函数的样式处理友元函数
#include <iostream> using namespace std; template <typename T> class Complex { //友元函数实现运算符重载 friend ostream& operator<<(ostream &out, Complex<T> &c); public: Complex(T a, T b); //运算符重载+ Complex<T> operator+(Complex<T> &c); //普通加法函数 Complex<T> myAdd(Complex<T> &c1, Complex<T> &c2); private: T a; T b; }; //友元函数的实现 template <typename T> ostream& operator<<(ostream &out, Complex<T> &c) { out<<c.a << " + " << c.b << "i"; return out; } //函数的实现 template <typename T> Complex<T>::Complex(T a, T b) { this->a = a; this->b = b; } template <typename T> Complex<T> Complex<T>::operator+(Complex<T> &c) { Complex temp(this->a + c.a, this->b + c.b); return temp; } template <typename T> Complex<T> Complex<T>::myAdd(Complex<T> &c1, Complex<T> &c2) { Complex temp(c1.a + c2.a, c1.b + c2.b); return temp; } int main() { Complex<int> c1(1,2); Complex<int> c2(3,4); Complex<int> c = c1 + c2; cout<<c<<endl; return 0; }
友元函数的定义写在类的外部--错误信息
Undefined symbols for architecture x86_64: "operator<<(std::__1::basic_ostream<char, std::__1::char_traits<char> >&, Complex<int>&)", referenced from: _main in demo1.o ld: symbol(s) not found for architecture x86_64 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文件
存放类的声明和函数的声明
#include <iostream> using namespace std; //类的前置声明 template <typename T> class Complex; //友元函数的声明 template <typename T> ostream& operator<<(ostream &out, Complex<T> &c); template <typename T> class Complex { //友元函数实现运算符重载 friend ostream& operator<< <T> (ostream &out, Complex<T> &c); public: Complex(T a, T b); //运算符重载+ Complex<T> operator+(Complex<T> &c); //普通加法函数 Complex<T> myAdd(Complex<T> &c1, Complex<T> &c2); private: T a; T b; };
demo2.hpp文件
包括模板函数的实现
#include "demo2.h" //友元函数的实现 template <typename T> ostream& operator<<(ostream &out, Complex<T> &c) { out<<c.a << " + " << c.b << "i"; return out; } //函数的实现 template <typename T> Complex<T>::Complex(T a, T b) { this->a = a; this->b = b; } template <typename T> Complex<T> Complex<T>::operator+(Complex<T> &c) { Complex temp(this->a + c.a, this->b + c.b); return temp; } template <typename T> Complex<T> Complex<T>::myAdd(Complex<T> &c1, Complex<T> &c2) { Complex temp(c1.a + c2.a, c1.b + c2.b); return temp; }
main.cpp文件
需要调用hpp文件
#include <iostream> using namespace std; #include "demo2.hpp" int main() { Complex<int> c1(1,2); Complex<int> c2(3,4); Complex<int> c = c1 + c2; cout<<c<<endl; return 0; }
原文链接:https://www.jianshu.com/p/70ca94872418
原文地址:https://www.cnblogs.com/HDK2016/p/11368592.html
- RedRabbit——基于BrokerPattern服务器框架
- C++任务队列与多线程
- 游戏服务器ID生成器组件
- Java8新特性——Lambda表达式(一)
- C++ FFLIB之FFRPC:多线程&多进程的scalability探索
- ffrpc-c++进程间(服务器端、客户端)通信框架
- Docker入门实战(一)——Docker常用命令
- C++使用ffpython嵌入和扩展python
- 前端防御从入门到弃坑——CSP变迁
- python的解释器spython介绍
- OpenCV人脸识别之二:模型训练
- 游戏服务器设计之NPC系统
- ffrpc的php客户端lib
- OpenCV人脸识别之三:识别自己的脸
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- 04 . Filebeat简介原理及配置文件和一些案例
- 05 . ELK Stack+Redis日志收集平台
- python开发【第一篇】
- 内置函数--bin() oct() int() hex()
- 08 . Prometheus+Grafana监控haproxy+rabbitmq
- 内置函数值 -- chr() ord() -- 字符和ascii的转换
- python内置函数-compile()
- 02 . Shell变量和逻辑判断及循环使用
- Python内置函数(21)——filter
- 内置函数 -- filter 和 map
- 内置函数--global() 和 local()
- python file文件操作--内置对象open
- 07 . Prometheus监控Memcached并配置Grafana
- 内置函数 -- bytes -- 字节码与字符串相互转换
- 01 . Shell详细入门介绍及简单应用