首页 > 编程语言 >C++:20---类模板(template)

C++:20---类模板(template)

时间:2022-11-01 14:32:21浏览次数:41  
标签:20 成员 C++ --- Blob template Foo class 模板

一、类模板与模板类

  • 类模板:一个模板(是模板)
  • 模板类:调用类模板生成的类对象(是类实体),也称为类模板的实例化

类模板的定义:

  • 与函数模板的定义是一样的


  1. template <typename T>
  2. class Blob
  3. {
  4. public:
  5. Blob();
  6. Blob(std::initializer_list<T> i);
  7. };


模板类的使用:

  • 在定义类时,使用到类名的地方都需要显示的给出模板类的类型,格式为<>


  1. int main()
  2. {
  3. Blob<int> ia;
  4. Blob<int> ia2 = { 1,2,3 };
  5. Blob<double>* ia4 = new Blob<double>{ 1.1,3.14 };
  6. Blob<string> ia3 = { "Hello","World" };

  7. return 0;
  8. }


二、模板类的成员函数

  • 如果模板类的成员函数在类内声明,而在类外定义,需要遵循以下规则:在函数前也在加上模板列表,且类名限定符后面给出<>
  1. template <typename T>
  2. class Blob
  3. {
  4. public:
  5. Blob();
  6. Blob(std::initializer_list<T> i);
  7. T func(T const &str);//在类内声明
  8. };

  9. //类外定义
  10. template <typename T>
  11. T Blob<T>::func(T const &str)
  12. {
  13. }

类模板中使用其它模板类型


  1. template <typename T> class Blob{
  2. template <typename It> Blob(It b, It e);//构造函数的参数使用其它模板类型
  3. };

  4. template <typename T>
  5. template <typename It>
  6. Blob<T>::Blob(It b, It e):data(std::make_shared<std::vector<T>>(b,e))
  7. {

  8. }
  9. int main()
  10. {
  11. vector<long> vi = { 0,1,2 };
  12. list<const char*> w = { "Hello","World" };

  13. Blob<int> a1(vi.begin(), vi.end());
  14. Blob<string> a2(w.begin(), w.end());
  15. return 0;
  16. }


三、友元:类模板中的友元

  • 一个类模板中也可以拥有友元(友元类/友元函数)
  • 下面只有当与Blob类型相同的BlobPtr类和operator==函数才可以成为Blob模板类的友元
  1. template <typename T> class BlobPtr;
  2. template <typename T> class Blob;
  3. template <typename T>
  4. bool operator==(const Blob<T>&, const Blob<T>&)
  5. {

  6. }

  7. template <typename T> class Blob
  8. {
  9. friend class BlobPtr<T>; //使BlobPtr模板类成为Blob模板类的友元
  10. friend bool operator==(const Blob<T>&, const Blob<T>&);//使operator函数成为Blob模板类的友元
  11. };

  12. template <typename T> class BlobPtr
  13. {
  14. };

四、友元:通用和特定的模板友元关系

  • 模板有需要复杂的关系,下面列出两个实例
  1. template <typename T> class Pal;

  2. class C
  3. {
  4. friend class Pal<C>; //用类C实例化的Pal是C的友元

  5. template <typename T> friend class Pal2; //Pal2的所有实例都是C的友元
  6. };
  7. template <typename T> class C2
  8. {
  9. friend class Pal<T>; //与C2相同类型的实例化Pal才是C2的友元

  10. template <typename X> friend class Pal2;//任何类型实例化的Pal2对象都是C2的友元,因为模板参数列表不同

  11. friend class Pal3;//Pal3是一个非模板类,它是所有类型C2实例化的友元
  12. };

五、类模板的static成员

  • 与任何其他类一样,类模板可以声明static成员
  • 例如:下面Foo类模板中定义了一个static函数和static变量
  1. template <typename T> class Foo
  2. {
  3. public:
  4. static std : size_t count() { return ctr; }
  5. private:
  6. static std::size_t ctr;
  7. };
  • 因为类的static成员变量只可在类内定义,在类外初始化。所以模板来的static变量也要在类外初始化,初始化时需要加上模板参数列表,例如下面代码,当一个特定的模板实例化Foo时,其ctr被初始化为0
  1. template <typename T>
  2. std::size_t Foo<T>::ctr = 0; //定义并初始化
  • 静态成员的调用
  1. Foo<int> fi; //实例化Foo<int>类和static数据成员ctr
  2. auto ct=Foo<int>::count();//实例化Foo<int>::count
  3. ct=fi.count(); //使用Foo<int>::count,与上面的意义是相同
  4. ct=Foo::count(); //错误,Foo没有指出使用哪个模板实例化
  • 类模板的static成员的特点:当一个类给出模板实例化之后,与这个类实例化类型相同的类共享一样的静态成员

Foo<int> f1,f2,f3; //f1,f2,f3共享Foo<int>::count()和Foo<int>::str

六、使用类的类型成员(::符号)

引入:

  • 当我们通过作用域符访问的名字是类型还是static成员,编译器会自动识别,例如:

string::size_type //编译器知道我们要访问string类中的size_type数据类型

  • 但是对于模板就不能使用这种方法了,例如:


  1. //编译器不知道size_type是一个static数据成员还是一种数据类型,因此产生二义性
  2. T::size_type * p;


  • 默认情况下,C++语言假定通过作用域运算符访问的名字不是数据类型,而是数据成员。所以如果我们希望使用一个模板类型参数的类型成员,就必须显式地告诉编译器改名字是一个类型。需要通过typename来实现这一点
  • 例如下面的top函数:


  1. template<typename T>
  2. typename T::value_type top(const T&c)
  3. {
  4. if (!c.empty())
  5. return c.back();
  6. else
  7. return typename T::value_type();
  8. }



七、成员模板

  • 一个类可以包含模板类型的成员函数,这种成员称为“成员模板”
  • 注意:成员模板不能为虚函数

①普通(非模板)类的成员模板

  • 概念:我们可以在一个非模板类中定义一个成员模板

演示案例

  • 默认的情况下,unique_ptr会调用元素的析构函数来删除元素。下面我们定义了一个删除器,删除器使用operator()接收一个元素指针,并将该元素进行delete


  1. //函数对象类,对给定指针执行delete
  2. class DebugDelete
  3. {
  4. public:
  5. DebugDelete(std::ostream &s=std::cerr):os(s){} //构造函数
  6. //根据传入的参数进行delete
  7. template <typename T>
  8. void operator()(T* p)const //成员模板
  9. {
  10. os << "deleting unique_ptr" << endl;
  11. delete p;
  12. }
  13. private:
  14. std::ostream &os;
  15. };


  • 下面是基本的使用格式:


  1. int main()
  2. {
  3. double *p = new double;
  4. DebugDelete d;
  5. d(p); //调用DebugDelete::operator()(double*)释放p

  6. int *ip = new int;
  7. DebugDelete()(ip); //在一个临时DebugDelete对象上调用operator()(int*)

  8. return 0;
  9. }


  • 下面我们将这个类作为unique_ptr的删除器来使用


  1. int main()
  2. {
  3. //一个类型为int的unique_ptr对象,DebugDelete作为其删除器
  4. unique_ptr<int, DebugDelete> p(new int, DebugDelete());

  5. //一个类型为string的unique_ptr对象,DebugDelete作为其删除器
  6. unique_ptr<std::string, DebugDelete> sp(new std::string, DebugDelete());

  7. return 0;
  8. }


②类模板的成员模板

  • 概念:对于类模板,我们也可以为其定义成员模板。在此情况下,类和成员各自有自己的、独立的模板参数

演示案例

  • 例如下面Blob是一个类模板,模板类型为T数据成员vector的类型也为T。另外其构造函数也是一个模板,其接受的模板类型为It


  1. template<typename T>
  2. class Blob {
  3. public:
  4. template<typename It>
  5. Blob(It b, It e); //构造函数接受一个迭代器区间,用来初始化data
  6. private:
  7. std::vector<T> data;
  8. };


  • 现在我们在类的外部定义构造函数,由于类模板与成员函数都是模板,因此在外部定义时需要分别同时给出这两个模板的模板参数列表
  • 实例化成员模板:为了实例化一个类模板的成员模板,我们必须同时提供类和函数模板的实参。见下面的演示案例,其中:
  • a1:Blob的类型为int,构造函数的类型为int*
  • a2:Blob的类型为int,构造函数的类型为vector<long>::iterator
  • a3:Blob的类型为string,构造函数的类型为list<const char*>::iterator


标签:20,成员,C++,---,Blob,template,Foo,class,模板
From: https://blog.51cto.com/u_14934686/5813660

相关文章

  • C语言: ---windows下VS Debug调试
    首先我先列出来常用的一些命令或者键盘控制:F5开始调试,执行到断点Shift+F5停止调试F9在光标所在行添加断点Shift+F9QuickWatchShiftCtrlF9deleteall断点F10单......
  • Redis:02---安装Redis(Linux+Windows+Docker)
    Linux安装:一、安装方式1(下载源码编译安装)第一步:从下面的网址中下载Redis最新稳定版本的源代码sudowgethttp://download.redis.io/redis-stable.tar.gz第二步:下载完之后解......
  • vs2013配置python_vs2013如何安装python
    vs2013如何安装python?步骤如下:1、安装PTVS:下载PTVS①找到下图位置,下载PythonToolsforVS2013地址:https://github.com/Microsoft/PTVS/releases/v2.2.2 ②安装RT......
  • 20221031&20221101 Keras
    周末长安杯加上组网实验信安数基上机计网翻转课堂核酸S12半决赛,小摆几天......
  • Authentication Required on Ubuntu 20.04
    Background 当我使用remote桌面连接到Ubuntu系统的时候,总是频繁的跳出AuthenticationRequired窗口,十分影响操作: Onceyouareloggedinmainlythesystemaskfo......
  • 【c&c++】 linux C之basename, dirname函数
    环境(centos6.10):$uname-r2.6.32-754.el6.x86_64函数定义:///usr/include/libgen.hexternchar*__xpg_basename(char*__path)__THROW;#definebasename......
  • leetcode111-二叉树的最小深度
    111.二叉树的最小深度 这道题相比 104.二叉树的最大深度 还是难上一些的,但也不算太难。BFS/***Definitionforabinarytreenode.*structTreeNode{......
  • STC32G12K128-CAN
    第二部分,因为需要和其他部件通过CAN进行交互,此处记录一下STC32-CAN驱动部分,毕竟是国产芯片,数据手册看起来非常方便。希望咱们国产芯片越做越好哈此处参考了芯片手册的例子......
  • 2021ICPC济南
    2021ICPC济南时隔一年再来补题,金牌题以下还是可以补的。C(组合数学,DP,贪心)题意两人每次从序列中取数字,希望自己拿到的数字和最大。求可行的操作序列方案数。思路看起来......
  • 动态规划-公共子序列
    公共子序列是二维动态规划的典型问题,一般用了求两个字符串的相似程度。我们看一个案例:案例1:给定两个字符串 text1和 text2,返回这两个字符串的最长公共子序列的长度......