• 类模板
  • Stack 类
  • 类模板
    • 格式
    • 类模板的应用
    • 类模板的多文件实现
  • 类模板的友元
    • 类模板中的友元在.h
    • 类模板中的友元在.cpp
    • hpp
  • STL入门

类模板

Stack 类

我们先给出我们之前实现的栈结构:

#include <iostream>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
using namespace std;
class Stack
{public:Stack(int size = 1024){space = new int[size];top = 0;}~Stack() {delete[]space;}bool isEmpty() {return top == 0;}bool isFull() {return top == 1024;}void push(int data) {space[top++] = data;}int pop() {return space[--top];}
private:int* space;int top;
};
int main()
{Stack s(100);for (int i = 0; i < 10; ++i) {if (!s.isFull())s.push(i);}while (!s.isEmpty())cout << s.pop() << endl;return 0;
}

运行结果为:

上面代码中如果我们要对于栈结构进行泛化,最主要的就是对于int * space 存储空间的泛化。

Stack 类模板化,可以 push 和 pop 不同的数据类型。主要由几个因素需要把控。

只需要保持栈中的空间元素类型,压入元素类型,弹出元素类型,三者保持一致就能够实现泛化。

类模板

格式

template<typename T> class ClassName
{void func(T );
};template<typename T> void ClassName<T>::func(T)
{}

类模板的应用

我们在函数模板中使用的时候先写函数模板,然后进行函数模板的实例化形成模板函数,然后对于实例化之后的模板函数进行调用。
myswap() --> myswap() --> myswap()(a,b)
函数模板 --> 模板函数 --> 函数调用

对比到类模板就是:
类模板 --> 模板类 --> 类对象的创建
stack --> stack --> stack s(100)

我们现在对于上面的栈类进行泛化:

#include <iostream>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>using namespace std;template <typename T>class Stack
{public:Stack(int size = 1024){space = new T[size];top = 0;}~Stack() {delete[]space;}bool isEmpty() {return top == 0;}bool isFull() {return top == 1024;}void push(T data) {space[top++] = data;}T pop() {return space[--top];}
private:T* space;int top;
};
int main()
{Stack<int>  s;//类模板的实例化   生成模板类  并且创建类对象for (int i = 0; i < 10; i++){if (!s.isFull())s.push(i * 11);}while(!s.isEmpty()){cout << s.pop() << endl;}return 0;
}

运行结果为:

类内函数的声明和定义分开的实现:

#include <iostream>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>using namespace std;template <typename T>      //类模板
class Stack
{public:Stack(int size = 1024);~Stack();bool isEmpty();bool isFull();void push(T data);T pop();private:T* space;int top;
};template <typename T>
Stack<T>::Stack(int size)
{space = new T[size];top = 0;
}template <typename T>
Stack<T>::~Stack()
{delete[]space;
}template <typename T>
bool Stack<T>::isEmpty()
{return top == 0;
}template <typename T>
bool Stack<T>::isFull()
{return top == 1024;
}template <typename T>
void Stack<T>::push(T data)
{space[top++] = data;
}template <typename T>
T Stack<T>::pop()
{return space[--top];
}int main()
{Stack<int>  s;//类模板的实例化   生成模板类  并且创建类对象for (int i = 0; i < 10; i++){if (!s.isFull())s.push(i * 10);}while (!s.isEmpty()){cout << s.pop() << endl;}return 0;
}

运行结果为:

类模板的多文件实现

main.cpp

#include <iostream>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include "mystack.h"
#include "mystack.cpp"
using namespace std;int main()
{Stack<int>  s;//类模板的实例化   生成模板类  并且创建类对象for (int i = 0; i < 10; i++){if (!s.isFull())s.push(i * 10);}while (!s.isEmpty()){cout << s.pop() << endl;}return 0;
}

mystack.cpp

#include "mystack.h"template <typename T>
Stack<T>::Stack(int size)
{space = new T[size];top = 0;
}template <typename T>
Stack<T>::~Stack()
{delete[]space;
}template <typename T>
bool Stack<T>::isEmpty()
{return top == 0;
}template <typename T>
bool Stack<T>::isFull()
{return top == 1024;
}template <typename T>
void Stack<T>::push(T data)
{space[top++] = data;
}template <typename T>
T Stack<T>::pop() {return space[--top];
}

mystack.h

#pragma once
template <typename T>      //类模板
class Stack
{public:Stack(int size = 1024);~Stack();bool isEmpty();bool isFull();void push(T data);T pop();private:T* space;int top;
};

运行结果为:

这里需要强调的是,我们在实现多文件变成的时候并不是简单的把之前类内函数的定义和实现分开,然后在多文件中编写,而是需要注意需要在main函数中加入#include “mystack.cpp”才能够编译成功和运行。

那么为什么是需要加上#include “mystack.cpp”

我们在类模板的友元中进行说明。

类模板的友元

类模板中的友元在.h

友元函数,实现在.h 文件中并不多见,但在模板中这样使用友元,就是一种常规则用法。

main.cpp

#include <iostream>
#include "mylist.h"
#include "mylist.cpp"using namespace std;
using namespace listspace;int main()
{GenericList<int> first_list(2);first_list.add(1);first_list.add(2);cout <<"first_list"<< first_list << endl;GenericList<char> second_list(10);second_list.add('A');second_list.add('B');second_list.add('C');cout << second_list<<second_list << endl;return 0;
}

mylist.cpp

#ifndef __MYLIST_ CPP__
#define __MYLIST_ CPP__
#include <iostream>
#include <cstdlib>
#include "mylist.h"using namespace std;
namespace listspace
{template<class ItemType>GenericList<ItemType>::GenericList(int max): _maxLength(max), _curIdx(0){_item = new ItemType[max];}template<class ItemType>GenericList<ItemType>::~GenericList(){delete[] _item;}template<class ItemType>int GenericList<ItemType>::length() const{return (_curIdx);}template<class ItemType>void GenericList<ItemType>::add(ItemType new_item){if (full()){cout << "Error: adding to a full list.\n";exit(1);}else{_item[_curIdx] = new_item;_curIdx = _curIdx + 1;}}template<class ItemType>bool GenericList<ItemType>::full() const{return (_curIdx == _maxLength);}template<class ItemType>void GenericList<ItemType>::erase(){_curIdx = 0;}
}
#endif

mylist.h

#ifndef __MYLIST_H H__
#define __MYLIST_H H__
#include <iostream>
#include <ostream>
using namespace std;namespace listspace
{template<class ItemType>class GenericList
{public:GenericList(int max);~GenericList();int length() const;void add(ItemType new_item);bool full() const;void erase();friend ostream &  operator<<(ostream & out,GenericList<ItemType>& list){for (int i = 0; i < list._curIdx; i++){out << list._item[i];}return out;}private:ItemType* _item;int _maxLength;int _curIdx;};
}//listspace
#endif //__MYLIST_H__

运行结果为:


使用类模板的友元时,在模板的.h文件类的声明处定义和实现友元函数。

类模板中的友元在.cpp

mylist.cpp

#ifndef __MYLIST_ CPP__
#define __MYLIST_ CPP__
#include <iostream>
#include <cstdlib>
#include "mylist.h"using namespace std;
namespace listspace
{template<class ItemType>GenericList<ItemType>::GenericList(int max): _maxLength(max), _curIdx(0){_item = new ItemType[max];}template<class ItemType>GenericList<ItemType>::~GenericList(){delete[] _item;}template<class ItemType>int GenericList<ItemType>::length() const{return (_curIdx);}template<class ItemType>void GenericList<ItemType>::add(ItemType new_item){if (full()){cout << "Error: adding to a full list.\n";exit(1);}else{_item[_curIdx] = new_item;_curIdx = _curIdx + 1;}}template<class ItemType>bool GenericList<ItemType>::full() const{return (_curIdx == _maxLength);}template<class ItemType>void GenericList<ItemType>::erase(){_curIdx = 0;}template<class ItemType>ostream & operator<< (ostream& out, GenericList<ItemType> & list){for (int i = 0; i < list._curIdx; i++){out << list._item[i];}return out;}
}
#endif

mylist.h

#ifndef __MYLIST_H H__
#define __MYLIST_H H__
#include <iostream>
#include <ostream>
using namespace std;
namespace listspace
{template<class ItemType>class GenericList;template<class ItemType>ostream & operator<<(ostream& out, GenericList<ItemType>& list);template<class ItemType>
class GenericList
{public:GenericList(int max);~GenericList();int length() const;void add(ItemType new_item);bool full() const;void erase();//友元函数声明friend ostream& operator<< <>(ostream& out, GenericList<ItemType>& list);private:ItemType* _item;int _maxLength;int _curIdx;};
}//listspace
#endif //__MYLIST_H__

main.cpp

#include <iostream>
#include "mylist.h"
#include "mylist.cpp"using namespace std;
using namespace listspace;
int main()
{GenericList<int> first_list(2);first_list.add(1);first_list.add(2);cout <<"first_list"<< first_list << endl;GenericList<char> second_list(10);second_list.add('A');second_list.add('B');second_list.add('C');cout << second_list<<second_list << endl;return 0;
}

运行结果为:

在类模板的 .cpp 文件中实现友元的时候需要进行以下操作:
① 在类中声明<> 表明是一个空体声明。
② 在类外实现,和其他函数实现相同,需要加上类型模板。
③ 在类的声明的前面,对类模板的友元函数作前向声明。并且在其前面作类的前向声明。

所以一般建议读者在.h文件中实现类模板的友元。

但是呢,我们平时在使用到时候很少会有#include “mylist.cpp”这样的操作,所以我们引入hpp

hpp

由于编译器需要通过这些"模板"为实例化类型生成实际的方法代码,因此任何使用了模板的源代码文件中,编译器都应该能同时访问类模板定义和方法定义。

C++中的编译是以文件为单位的,然后链接阶段完成链接。如果模板的声明与实现分开,这种机制显然会导致看不到模板的全貌,而致编译失败。所以常将类模板定义和方法定义放到一起,该类模板文件的后缀常为.hpp,以示与普通文件的区别。在使用的时候,#include"xx.hpp"。

代码演示:

mylist.hpp

#ifndef __MYLIST_H H__
#define __MYLIST_H H__
#include <iostream>
#include <ostream>
using namespace std;namespace listspace
{template<class ItemType>class GenericList;template<class ItemType>ostream & operator<<(ostream& out, GenericList<ItemType>& list);template<class ItemType>class GenericList{public:GenericList(int max);~GenericList();int length() const;void add(ItemType new_item);bool full() const;void erase();friend ostream& operator<< <>(ostream& out, GenericList<ItemType>& list);private:ItemType* _item;int _maxLength;int _curIdx;};
}//listspace
#endif //__MYLIST_H__#ifndef __MYLIST_ CPP__
#define __MYLIST_ CPP__
#include <iostream>
#include <cstdlib>
#include "mylist.h"using namespace std;
namespace listspace
{template<class ItemType>
GenericList<ItemType>::GenericList(int max): _maxLength(max), _curIdx(0)
{_item = new ItemType[max];
}
template<class ItemType>
GenericList<ItemType>::~GenericList()
{delete[] _item;
}
template<class ItemType>
int GenericList<ItemType>::length() const
{return (_curIdx);
}
template<class ItemType>
void GenericList<ItemType>::add(ItemType new_item)
{if (full()){cout << "Error: adding to a full list.\n";exit(1);}else{_item[_curIdx] = new_item;_curIdx = _curIdx + 1;}
}
template<class ItemType>
bool GenericList<ItemType>::full() const
{return (_curIdx == _maxLength);
}   template<class ItemType>
void GenericList<ItemType>::erase()
{_curIdx = 0;
}template<class ItemType>
ostream& operator<< (ostream& out, GenericList<ItemType>& list)
{for (int i = 0; i < list._curIdx; i++){out << list._item[i];}return out;
}
}
#endif

main.cpp

#include <iostream>
#include "mylist.hpp"using namespace std;
using namespace listspace;
int main()
{GenericList<int> first_list(2);first_list.add(1);first_list.add(2);cout <<"first_list"<< first_list << endl;GenericList<char> second_list(10);second_list.add('A');second_list.add('B');second_list.add('C');cout << second_list<<second_list << endl;return 0;
}

运行结果为:

模板通过会将声明和实现放在一个文件中,及就是.hpp中。这样做的原因就是,C语言和C++的编译模式是按照文件进行编译的。

模板在进行实例化的时候,是需要看到整个模板的全部内容,及就是模板的定义和实现的全部代码。所以如果只有.h的话,实例化的时候仅仅只是看到模板的声明,并不能看到模板的实现,所以需要使用.hpp来实现。把模板的声明和实现放在一起。

也就是说在任何需要实例化的地方,都需要看到模板的全部的声明和定义。

STL入门

代码演示:

#include <iostream>
#include <vector>
#include <stdlib.h>
#include <time.h>
#include <algorithm>
using namespace std;int main()
{srand(time(nullptr));vector<int> vi; //类模板的实例化,生成模板类,并且创建类对象 vifor (int i = 0; i < 10; i++){vi.push_back(rand() % 100);}sort(vi.begin(), vi.end(), [](int x, int y) {return x < y; });for (auto i : vi)cout << i << endl;return 0;
}

运行结果:

引入内存管理机制,一开始不需要指定大小。

C++模板:类模板和类模板的友元【C++模板】(57)相关推荐

  1. C++知识点57——类模板(2、类模板的局部特化与默认模板实参)

    接上一篇文章https://blog.csdn.net/Master_Cui/article/details/111824064 四.类模板的局部特化 类模板可以被局部特化(只指定部分模板参数而不指定 ...

  2. C++ Primer 5th笔记(chap 16 模板和泛型编程)类模板和友元

    1. 定义 如果一个类模板包含一个非模板友元,则友元被授权可以访问所有模板实例.如果友元自身是模板,类可以授权给所有友元模板实例,也可以只授权给特定实例. 1.1 一对一友好关系 类模板与另一个(类或 ...

  3. C++ Primer 5th笔记(chap 16 模板和泛型编程)类模板成员函数的实例化

    1. 默认情况下,一个类模板的成员函数只有当程序用到它时才进行实例化. (即使某种类型不能完全符合模板操作的要求(参见9.2节,第294页),我们仍然能用该类型实例化类todo). eg. //实例化 ...

  4. C++ Primer 5th笔记(chap 16 模板和泛型编程)类模板定义

    1. 定义 类似函数模板,类模板以关键字template开始,后跟模板参数列表.在类模板(及其成员)的定义中,我们将模板参数当作替身,代替使用模板时用户需要提供的类型或值: template < ...

  5. 类模板,多种类型的类模板,自定义类模板,类模板的默认类型,数组的模板实现,友元和类模板,友元函数,类模板与静态变量,类模板与普通类之间互相继承,类模板作为模板参数,类嵌套,类模板嵌套,类包装器

     1.第一个最简单的类模板案例 #include "mainwindow.h" #include <QApplication> #include <QPush ...

  6. 模板原理和操作数据类的观点【艰难的一天,慢慢的会过去的】

    1.模板原理:视图类[将数据输出到模板中,实现对视图的控制] smarty的类实现对视图的控制[展示和smarty的基本语法:smarty需要它的库进行支持] 面向对象的编程中对象的访问和类的访问本质 ...

  7. 什么是函数模板以及什么是类模板

    [1]模板的含义 (1) 模板就是实现代码重用的机制的一种工具,它可以实现类型参数化,即把类型定义为参数,从而实现了真正的代码可重用性. (2) 模板可以分为两类,一个是函数模板,另外一个是类模板. ...

  8. 响应式织梦模板装修装饰设计类网站

    模板介绍: 织梦内核开发的模板,该模板属于企业通用类.家装.装修.装饰.装潢类企业都可使用, 这款模板使用范围极广,不仅仅局限于一类型的企业,你只需要把图片和产品内容: 换成你的,颜色都可以修改,改完 ...

  9. 响应式织梦模板日化食品零食类网站

    模板介绍: 织梦内核开发的模板,该模板属于企业通用类.日化.食品.零食类企业都可使用, 这款模板使用范围极广,不仅仅局限于一类型的企业,你只需要把图片和产品内容: 换成你的,颜色都可以修改,改完让你耳 ...

  10. 响应式织梦模板测量试验机类网站

    模板介绍: 织梦内核开发的模板,该模板属于企业通用类.测量机.试验机.显微镜类企业都可使用, 这款模板使用范围极广,不仅仅局限于一类型的企业,你只需要把图片和产品内容: 换成你的,颜色都可以修改,改完 ...

最新文章

  1. redis cluster 安装配置
  2. Anaconda简单入门
  3. Ubuntu13.04 配置smb服务器-new
  4. 小手取红色球C语言程序,C语言程序设计例精编.doc
  5. 从上往下 流式布局_揭秘做好网站结构优化的4步(下)
  6. Oracle数据库的视图
  7. sql server简单查询
  8. 元素在父元素内垂直居中的思路
  9. mysql排序行号_mysql 取得行号后再排序
  10. springclould项目启动报错Could not resolve placeholder
  11. Go中数字转换字符串的正确姿势
  12. 《GPUPro》笔记
  13. ORACLE 归档日志打开与关闭
  14. 由来源地址决定显示什么页面
  15. OpenCV之LBP算法学习
  16. php调用平安银行接口,PHP-Java-Bridge的使用(平安银行支付功能专版)
  17. ARP协议,ARP攻击的原理,网络执法官的具体实现
  18. 初中计算机excel考试系统,基于Excel构建计算机考试系统
  19. 二进制数转整数、整数转二进制数、二进制数求模
  20. JS 实现图层模式覆盖效果

热门文章

  1. 美国大学生数学建模竞赛15大热点问题
  2. 【攻防世界007】simple-check-100
  3. 2020-11-22(操作系统——页面置换算法)
  4. Windbg新手入坑指南
  5. 1.14 Stream操作Collection集合
  6. Codeforces Round #494 (Div. 3)【未完结】
  7. Acwing第 36 场周赛【完结】
  8. Codeforces Round #744 (Div. 3)【A-D E的题解】
  9. 力扣: 268. 丢失的数字
  10. MySQL查询的进阶操作--子查询(内查询)