类模板语法知识体系梳理(包含大量常犯错误demo,尤其滥用友元函数的错误)
demo 1
- #include <iostream>
- #include <cstdio>
- using namespace std;
- //template <typename T>
- class Complex
- {
- friend ostream &operator << (ostream &out, Complex &c2);
- public:
- Complex(int a, int b) : a(a), b(b) {}
- Complex operator + (Complex &c2)
- {
- Complex tem(a + c2.a, b + c2.b);
- return tem;
- }
- void printCom()
- {
- cout << a << " + " << b << "i\n";
- }
- private:
- int a;
- int b;
- };
- ostream &operator << (ostream &out, Complex &c2)
- {
- out << c2.a << " + " << c2.b << "i\n";
- return out;
- }
- int main()
- {
- Complex c1(1, 2);
- c1.printCom();
- Complex c2(3, 4);
- c2.printCom();
- Complex c3 = c1 + c2;
- c3.printCom();
- cout << c3 << endl;
- return 0;
- }
上述是一个简单的复数类,并重载了+运算符和 << 运算符。
下面拓展,变成模板类:
demo 2
- #include <iostream>
- #include <cstdio>
- using namespace std;
- template <typename T>
- class Complex
- {
- friend Complex& mySub(Complex &c1, Complex &c2) // 写在里面没什么问题
- {
- Complex tmp(c1.a - c2.a, c1.b - c2.b);
- return tmp;
- }
- friend ostream &operator << (ostream &out, Complex &c2)
- {
- out << c2.a << " + " << c2.b << "i\n";
- return out;
- }
- public:
- Complex(T a, T b) : a(a), b(b) {}
- Complex operator + (Complex &c2)
- {
- Complex tem(a + c2.a, b + c2.b);
- return tem;
- }
- void printCom()
- {
- cout << a << " + " << b << "i\n";
- }
- private:
- T a;
- T b;
- };
- // 运算符重载的正规写法
- // 重载 << >> 只能用友元函数,其他运算符重载都要写成成员函数,不要滥用友元函数
- /* 这部分定义必须写进类的内部
- ostream &operator << (ostream &out, Complex &c2)
- {
- out << c2.a << " + " << c2.b << "i\n";
- return out;
- }
- */
- int main()
- {
- // 需要把模板类具体化之后才能定义对象,C++编译器需要分配内存
- Complex<int> c1(1, 2);
- c1.printCom();
- Complex<int> c2(3, 4);
- c2.printCom();
- Complex<int> c3 = c1 + c2;
- c3.printCom();
- cout << c3 << endl;
- // 滥用友元函数
- {
- Complex<int> c3 = mySub(c1, c2);
- cout << c3;
- }
- return 0;
- }
这样看起来问题也不大。
继续,把所有成员函数都放到类外部,先还是写在同一个cpp文件中:
demo 3
- #include <iostream>
- #include <cstdio>
- using namespace std;
- template <typename T>
- class Complex; // 解决mySub友元函数的滥用
- template <typename T>
- Complex<T> mySub(Complex<T> &c1, Complex<T> &c2); // 解决mySub友元函数的滥用
- template <typename T>
- class Complex
- {
- // 这个友元函数是滥用
- friend Complex<T> mySub<T>(Complex<T> &c1, Complex<T> &c2); // 写在里面没什么问题
- // 这样55行会报错
- //friend ostream &operator << (ostream &out, Complex &c2);
- // 解决方案
- friend ostream &operator << <T> (ostream &out, Complex &c2);
- public:
- Complex(T a, T b);
- Complex operator + (Complex &c2);
- void printCom();
- private:
- T a;
- T b;
- };
- // 构造函数拿到写在类的外部
- template <typename T>
- Complex<T>::Complex(T a, T b)
- {
- this->a = a;
- this->b = b;
- }
- template <typename T>
- void Complex<T>::printCom()
- {
- cout << a << " + " << b << "i\n";
- }
- // 注意函数参数和函数返回值都需要进行类型具体化
- template <typename T>
- Complex<T> Complex<T>::operator + (Complex<T> &c2)
- {
- Complex tem(a + c2.a, b + c2.b);
- return tem;
- }
- // 报错的本质:模版是两次编译运行的,第一次生成的函数头和第二次生成的函数头不一样
- // 友元函数实现 << 运算符重载
- template <typename T>
- ostream &operator << (ostream &out, Complex<T> &c2)
- {
- out << c2.a << " + " << c2.b << "i\n";
- return out;
- }
- // 报错
- /*
- 1>templateComplex2.obj : error LNK2019: 无法解析的外部符号
- "class std::basic_ostream<char,struct std::char_traits<char> >
- & __cdecl operator<<(class std::basic_ostream<char,struct std::char_traits<char> >
- &,class Complex<int> &)" (??6@YAAAV?$basic_ostream@DU?$char_traits@D@std@@@std@@AAV01@AAV?$Complex@H@@@Z),
- 该符号在函数 _main 中被引用
- */
- //////////////////////////////////////////////////////
- // 滥用友元函数
- template <typename T>
- Complex<T> mySub(Complex<T> &c1, Complex<T> &c2)
- {
- Complex<T> tmp(c1.a - c2.a, c1.b - c2.b);
- return tmp;
- }
- // 报错。。。。。。
- // 解决方案,做前置声明
- int main()
- {
- // 需要把模板类具体化之后才能定义对象,C++编译器需要分配内存
- Complex<int> c1(1, 2);
- c1.printCom();
- Complex<int> c2(3, 4);
- c2.printCom();
- Complex<int> c3 = c1 + c2;
- c3.printCom();
- cout << c3 << endl;
- // 滥用友元函数
- {
- Complex<int> c3 = mySub(c1, c2);
- cout << c3;
- }
- return 0;
- }
demo 3出现了几个错误,代码中都注释了,尤其友元函数的滥用一定要注意,千万别再不能用友元函数的地方用友元函数,demo 3中出现的问题还都解决了,可是当把类写到.cpp和.h文件中还会出现新的问题,先总结在同一文件下:
所有的类模板函数写在类的外部,在一个cpp中
- //构造函数 没有问题
- //普通函数 没有问题
- //友元函数:用友元函数重载 << >>
- // friend ostream& operator<< <T> (ostream &out, Complex<T> &c3) ;
- //友元函数:友元函数不是实现函数重载(非 << >>)
- //1)需要在类前增加 类的前置声明 函数的前置声明
- template<typename T>
- class Complex;
- template<typename T>
- Complex<T> mySub(Complex<T> &c1, Complex<T> &c2);
- //2)类的内部声明 必须写成:
- friend Complex<T> mySub <T>(Complex<T> &c1, Complex<T> &c2);
- //3)友元函数实现 必须写成:
- template<typename T>
- Complex<T> mySub(Complex<T> &c1, Complex<T> &c2)
- {
- Complex<T> tmp(c1.a - c2.a, c1.b - c2.b);
- return tmp;
- }
- //4)友元函数调用 必须写成
- Complex<int> c4 = mySub<int>(c1, c2);
- cout << c4;
结论:友元函数只用来进行左移友移操作符重载。
归纳以上的介绍:可以这样声明和使用类模版:
1) 先写出一个实际的类。由于其语义明确,含义清楚,一般不会出错。
2) 将此类中准备改变的类型名(如int要改变为float或char)改用一个自己指定的虚拟类型名(如上例中的numtype)。
3) 在类声明前面加入一行,格式为:
template <class 虚拟类型参数>
如:
template <class numtype> //注意本行末尾无分号
class Compare
{…}; //类体
4) 用类模板定义对象时用以下形式:
类模板名<实际类型名> 对象名;
类模板名<实际类型名> 对象名(实参表列);
如:
Compare<int> cmp;
Compare<int> cmp(3,7);
5) 如果在类模板外定义成员函数,应写成类模板形式:
template <class 虚拟类型参数>
函数类型 类模板名<虚拟类型参数>::成员函数名(函数形参表列) {…}
关于类模板的几点说明:
1) 类模板的类型参数可以有一个或多个,每个类型前面都必须加class,如:
template <class T1,class T2>
class someclass
{…};
在定义对象时分别代入实际的类型名,如:
someclass<int,double> obj;
2) 和使用类一样,使用类模板时要注意其作用域,只能在其有效作用域内用它定义对象。
3) 模板可以有层次,一个类模板可以作为基类,派生出派生模板类。
类模板语法知识体系梳理(包含大量常犯错误demo,尤其滥用友元函数的错误)的更多相关文章
- C++ 类模板基础知识
类模板与模板类 为什么要引入类模板:类模板是对一批仅仅成员数据类型不同的类的抽象,程序员只要为这一批类所组成的整个类家族创建一个类模板,给出一套程序代码,就可以用来生成多种具体的类,(这类可以看作是类 ...
- Android7_安卓的知识体系梳理
最近梳理了一下安卓的知识体系,先构建一个整体性的认知,也作为以后的学习路线的依据. [一.从原理角度出发]1.Activity生命周期和启动模式2.View的事件体系与工作原理3.四大组件的工作过程4 ...
- C++_进阶之函数模板_类模板
C++_进阶之函数模板_类模板 第一部分 前言 c++提供了函数模板(function template.)所谓函数模板,实际上是建立一个通用函数,其函数类型和形参类型不具体制定,用一个虚拟的类型来 ...
- C++复习:函数模板和类模板
前言 C++提供了函数模板(function template).所谓函数模板,实际上是建立一个通用函数,其函数类型和形参类型不具体指定,用一个虚拟的类型来代表.这个通用函数就称为函数模板.凡是函数体 ...
- c++11-17 模板核心知识(二)—— 类模板
类模板声明.实现与使用 Class Instantiation 使用类模板的部分成员函数 Concept 友元 方式一 方式二 类模板的全特化 类模板的偏特化 多模板参数的偏特化 默认模板参数 Typ ...
- C++语法小记---类模板
类模板 类模板和函数模板类似,主要用于定义容器类 类模板可以偏特化,也可以全特化,使用的优先级和函数模板相同 类模板不能隐式推倒,只能显式调用 工程建议: 模板的声明和实现都在头文件中 成员函数的实现 ...
- 从输入URL到页面加载的过程?如何由一道题完善自己的前端知识体系!
前言 见解有限,如有描述不当之处,请帮忙指出,如有错误,会及时修正. 为什么要梳理这篇文章? 最近恰好被问到这方面的问题,尝试整理后发现,这道题的覆盖面可以非常广,很适合作为一道承载知识体系的题目. ...
- github上最全的资源教程-前端涉及的所有知识体系
前面分享了前端入门资源汇总,今天分享下前端所有的知识体系. 个人站长对个人综合素质要求还是比较高的,要想打造多拉斯自媒体网站,不花点心血是很难成功的,学习前端是必不可少的一个环节, 当然你不一定要成为 ...
- github上最全的资源教程-前端涉及的所有知识体系【转】
github上最全的资源教程-前端涉及的所有知识体系[转自:蓝猫的博客] 综合类 综合类 地址 前端知识体系 http://www.cnblogs.com/sb19871023/p/3894452.h ...
随机推荐
- Mongo Index
摘要 mongo 的索引非常强大,和关系型数据库索引没什么区别.这里主要介绍本人在mongo索引上的犯的错. 索引种类 1.单字段索引 2.复合索引 多个字段索引 如{name:1,address:1 ...
- 在做自动化测试之前你需要知道的,转自:http://www.cnblogs.com/fnng/p/3653793.html
什么是自动化测? 做测试好几年了,真正学习和实践自动化测试一年,自我感觉这一个年中收获许多.一直想动笔写一篇文章分享自动化测试实践中的一些经验.终于决定花点时间来做这件事儿. 首先理清自动化测试的概念 ...
- React Native自定义导航条
Navigator和NavigatorIOS 在开发中,需要实现多个界面的切换,这时候就需要一个导航控制器来进行各种效果的切换.在React Native中RN为我们提供了两个组件:Navigator ...
- Xcode7.3中SKAudioNode"诡异"初始化的解决
大熊猫猪·侯佩原创或翻译作品.欢迎转载,转载请注明出处. 如果觉得写的不好请多提意见,如果觉得不错请多多支持点赞.谢谢! hopy ;) 我没有在之前版本的Xcode中测试,不过很多人反映SKAudi ...
- Nginx的负载均衡 - 加权轮询 (Weighted Round Robin) 上篇
Nginx版本:1.9.1 我的博客:http://blog.csdn.net/zhangskd 算法介绍 来看一个简单的Nginx负载均衡配置. http { upstream cluster { ...
- Spring之Enterprise JavaBeans (EJB) integration
原文地址:需要FQ https://translate.google.com/translate?hl=zh-CN&sl=zh-CN&tl=zh-CN&u=http%3A%2F ...
- 使用Geolocation校正GDAL不支持的数据
对于低分数据来说,常用的校正方式就是给定数据的经纬度查找表来进行校正.在GDAL中,这种校正方式叫Geolocation array.常用的数据有国外的MODIS数据,国内的如风云系列(FY)和海洋系 ...
- 随机采样和随机模拟:吉布斯采样Gibbs Sampling实现文档分类
http://blog.csdn.net/pipisorry/article/details/51525308 吉布斯采样的实现问题 本文主要说明如何通过吉布斯采样进行文档分类(聚类),当然更复杂的实 ...
- Struts2知识点学习笔记
写给自己的话 好记性不如烂笔头,不可能说看了一遍视频就可以完全掌握的.留下这篇笔记,便于今后的复习吧. 1. 访问ServletAPI 访问ServletAPI(response,request,)的 ...
- 高通8x12平台开机画面制作工具
你可能在网上看到很到关于手动更换手机开机图片的文章,想想自己的开机画面是小两口,好基友的照片多么个性啊.但是你有没有发现,网上下载的什么"一键生成"之类的,在你的手机上不能用啊,( ...