前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >C++:20---类模板(template)

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

作者头像
用户3479834
发布2021-02-03 12:20:18
1.2K0
发布2021-02-03 12:20:18
举报
文章被收录于专栏:游戏开发司机

一、类模板与模板类

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

类模板的定义:

  • 与函数模板的定义是一样的
代码语言:javascript
复制
template <typename T>class Blob{public:Blob();Blob(std::initializer_list<T> i);};

模板类的使用:

  • 在定义类时,使用到类名的地方都需要显示的给出模板类的类型,格式为<>
代码语言:javascript
复制
int main(){Blob<int> ia;Blob<int> ia2 = { 1,2,3 };Blob<double>* ia4 = new Blob<double>{ 1.1,3.14 };Blob<string> ia3 = { "Hello","World" };
return 0;}

二、模板类的成员函数

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

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

代码语言:javascript
复制
template <typename T> class Blob{	template <typename It> Blob(It b, It e);//构造函数的参数使用其它模板类型};
template <typename T>template <typename It>Blob<T>::Blob(It b, It e):data(std::make_shared<std::vector<T>>(b,e)){
}int main(){vector<long> vi = { 0,1,2 };list<const char*> w = { "Hello","World" };
Blob<int> a1(vi.begin(), vi.end());Blob<string> a2(w.begin(), w.end());return 0;}

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

  • 一个类模板中也可以拥有友元(友元类/友元函数)
  • 下面只有当与Blob类型相同的BlobPtr类和operator==函数才可以成为Blob模板类的友元
代码语言:javascript
复制
template <typename T> class BlobPtr;template <typename T> class Blob;template <typename T>bool operator==(const Blob<T>&, const Blob<T>&){
}
template <typename T> class Blob{friend class BlobPtr<T>; //使BlobPtr模板类成为Blob模板类的友元friend bool operator==(const Blob<T>&, const Blob<T>&);//使operator函数成为Blob模板类的友元};
template <typename T> class BlobPtr{};

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

  • 模板有需要复杂的关系,下面列出两个实例
代码语言:javascript
复制
template <typename T> class Pal;
class C{friend class Pal<C>; //用类C实例化的Pal是C的友元
template <typename T> friend class Pal2; //Pal2的所有实例都是C的友元};
代码语言:javascript
复制
template <typename T> class C2{friend class Pal<T>; //与C2相同类型的实例化Pal才是C2的友元
template <typename X> friend class Pal2;//任何类型实例化的Pal2对象都是C2的友元,因为模板参数列表不同
friend class Pal3;//Pal3是一个非模板类,它是所有类型C2实例化的友元};

五、类模板的static成员

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

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

引入:

  • 当我们通过作用域符访问的名字是类型还是static成员,编译器会自动识别,例如:
代码语言:javascript
复制
string::size_type //编译器知道我们要访问string类中的size_type数据类型
  • 但是对于模板就不能使用这种方法了,例如:
代码语言:javascript
复制
//编译器不知道size_type是一个static数据成员还是一种数据类型,因此产生二义性T::size_type * p;

  • 默认情况下,C++语言假定通过作用域运算符访问的名字不是数据类型,而是数据成员。所以如果我们希望使用一个模板类型参数的类型成员,就必须显式地告诉编译器改名字是一个类型。需要通过typename来实现这一点
  • 例如下面的top函数:
代码语言:javascript
复制
template<typename T>typename T::value_type top(const T&c){	if (!c.empty())		return c.back();	else		return typename T::value_type();}

七、成员模板

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

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

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

演示案例

  • 默认的情况下,unique_ptr会调用元素的析构函数来删除元素。下面我们定义了一个删除器,删除器使用operator()接收一个元素指针,并将该元素进行delete
代码语言:javascript
复制
//函数对象类,对给定指针执行deleteclass  DebugDelete{public:DebugDelete(std::ostream &s=std::cerr):os(s){}   //构造函数	//根据传入的参数进行deletetemplate <typename T>void operator()(T* p)const //成员模板{os << "deleting unique_ptr" << endl;delete p;}private:std::ostream &os;};
  • 下面是基本的使用格式:
代码语言:javascript
复制
int main(){double *p = new double;DebugDelete d;d(p);               //调用DebugDelete::operator()(double*)释放p
int *ip = new int;DebugDelete()(ip); //在一个临时DebugDelete对象上调用operator()(int*)
return 0;}
  • 下面我们将这个类作为unique_ptr的删除器来使用
代码语言:javascript
复制
int main(){//一个类型为int的unique_ptr对象,DebugDelete作为其删除器unique_ptr<int, DebugDelete> p(new int, DebugDelete());
//一个类型为string的unique_ptr对象,DebugDelete作为其删除器unique_ptr<std::string, DebugDelete> sp(new std::string, DebugDelete());
return 0;}

②类模板的成员模板

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

演示案例

  • 例如下面Blob是一个类模板,模板类型为T数据成员vector的类型也为T。另外其构造函数也是一个模板,其接受的模板类型为It
代码语言:javascript
复制
template<typename T>class Blob {public:template<typename It>Blob(It b, It e);  //构造函数接受一个迭代器区间,用来初始化dataprivate:std::vector<T> data;};
  • 现在我们在类的外部定义构造函数,由于类模板与成员函数都是模板,因此在外部定义时需要分别同时给出这两个模板的模板参数列表
  • 实例化成员模板:为了实例化一个类模板的成员模板,我们必须同时提供类和函数模板的实参。见下面的演示案例,其中:
    • a1:Blob的类型为int,构造函数的类型为int*
    • a2:Blob的类型为int,构造函数的类型为vector<long>::iterator
    • a3:Blob的类型为string,构造函数的类型为list<const char*>::iterator
本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2020-12-07,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 游戏开发司机 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 一、类模板与模板类
    • 类模板的定义:
      • 模板类的使用:
      • 二、模板类的成员函数
        • 类模板中使用其它模板类型
        • 三、友元:类模板中的友元
        • 四、友元:通用和特定的模板友元关系
        • 五、类模板的static成员
        • 六、使用类的类型成员(::符号)
          • 引入:
          • 七、成员模板
            • ①普通(非模板)类的成员模板
              • 演示案例
                • ②类模板的成员模板
                  • 演示案例
                  领券
                  问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档