类模板

---恢复内容开始---

感觉C++ templates这本书写的不怎么好,挑写重点记一下:

1 与函数模板一样,只能在同一个头文件中声明和定义类。

类模板的声明

类模板的声明和函数模板的声明很相似:在声明之前,我们先(用一条语句)声明作为类型参数的标志符;我们继续使用T作为标志符;

template <typename T>
class Stack{
         ...
};

  在此,我们可以使用关键字class代替typename。

  在类模板的内部,T可以像其他任何类型一样,用于声明成员变量和成员函数。

  这个类的类型是Stack<T>,其中T是模板参数,因此,当在声明中需要使用该类的类型时,你必须使用Stack<T>.但是,如果在类模板自己的作用域中,我们可以直接使用模板名而不提供实参:

  例如:

template <typename T>
class Stack{
    ...
    Stack(Stack<T> const &);  //拷贝构造函数
    Stack<T> &operator==(Stack<T> const &);  //赋值运算符
};
可以简化为
template <typename T>
class Stack{
    ...
    Stack(Stack const &);  //拷贝构造函数
    Stack &operator==(Stack const &);  //赋值运算符
};

(这里感觉C++ templates中写的有点问题,上面是这样说的:当使用类名而不是类的类型时,就应该只用Stack;譬如,当你指定类的名称、类的构造函数、析构函数时,就应该使用Stack。)

按照C++ primer 中就不是这样的,只要在类模板的作用域中就都可以省略掉T。上机测试也没问题。。

  成员函数的实现

为了定义类模板的成员函数,你必须指定该成员函数是一个函数模板,而且你还需要使用这个类模板的完整限定符。因此,类型Stack<T>的成员函数push()的实现如下:

template<typename T>
void Stack<T>::push(Tconst &elem)
{
    elems.push_back(elem):
}

显然,对于类模板的任何成员函数,你都可以把它实现为内联函数,将它定义于类声明里面。例如:

template<typename T>
class Stack{
    ...
    void push(T const &elem){
        elems.push_back(elem);
    }
};

类模板的使用

注意:只有那些被调用的成员函数,才会产生这些函数的实例化代码,对于类模板,成员函数只有在被使用的时候才会被实例化。显然,这样可以节省空间和时间;另一个好处是:对于那些“未能提供所有成员函数中所有操作的”类型,你也可以使用该类型来实例化类模板,只要对那些“未能提供某些操作的”成员函数,模板内部不使用就可以。例如,某些类模板中的成员函数会使用operator<来排序元素;如果不调用这些“使用operator<的”成员函数,那么对于没有定义operator<的类型,也可以被用来实例化该类模板。

类模板的特化

你可以用模板实参来特化类模板,和函数模板的重载类似,通过特化类模板,你可以优化基于某种特定类型的实现,或者克服某种特定类型在实例化类模板时所出现的不足。另外,如果要特化一个类模板,你还要特化该类模板的所有成员函数。

为了特化一个类模板,你必须在起始处声明一个template<>,接下来声明用来特化类模板的类型。这个类型被用作模板实参,且必须在类名的后面直接指定:

template<>
class Stack<std::string>{
           ........
};

  进行类模板的特化时,每个成员函数都必须重新定义为普通函数,原来模板函数中的每个T也相应地被进行特化的类型取代:

void Stack<std::string>::push(std::string const &elem)
{
    elems.push_back(elem);
}

  注意:在全特化类模板时,成员的特化前面不需要加template<>。

局部特化


类模板可以被局部特化。你可以在特定的环境下指定类模板的特定实现,并且要求某些模板参数仍然必须由用户来定义。例如类模板:

  

template<typename T1,typename T2>
class MyClass{
        .....
};

  就可以有下面几种局部特化:

//局部特化:两个模板参数具有相同的类型

template<typename T>
class MyClass<T,T>{
        ...
};
//局部特化:第2个模板参数的类型是int
template<typename T>
class MyClass<T,int>{
        ...
};

//局部特化:两个模板参数都是指针类型
template<typename T1,typename T2>
class MyClass<T1*,T2*>{
        ...
};

  下面的例子展示各种声明会使用哪个模板:

MyClass<int,float> mif;   //MyClass<T1,T2>
MyClass<float,float> mff; //MyClass<T,T>
MyClass<float,int> mfi;  //MyClass<T,int>
MyClass<int*,float*> mp; //MyClass<T1*,T2*>

  如果同时有多个局部特化同等程度地匹配某个声明,那么就称该声明具有二义性:

MyClass<int,int> m; //错误:同等匹配MyClass<T,T>和MyClass<T,int>
MyClass<int*,int*> m;//错误:同等匹配MyClass<T,T>和MyClass<T1*,T2*>

  为了解决第2中二义型,你可以另外提供一个指向相同类型指针的特化:

template<typename T>
MyClass<T*,T*>{
        ...
};

  缺省模板实参

对于类模板,你还可以为模板参数定义缺省值;这些值就被称为缺省模板实参;而且,它们还可以引用之前的模板参数。例如,在Stack<>中,你可以把用于管理元素的容器定义为第2个模板实参,并且使用std::vector<>作为它的缺省值:

template<typename T,typename CONT=std::vector<T> >(记得加空格)
class Stack{
    ....
    public:
        void push(T const &);
};

template<typename T,typename CONT>
void Stack<T,CONT>::push(T const& elem)
{
    elems.push_back(elem);
}

  可以看到:我们的类模板含有两个模板参数,因此每个成员函数的定义都必须具有这两个参数。

 

小结:

 

  • 类模板是具有如下性质的类:在类的实现中,可以有一个或多个类型还没与被指定。
  • 为了使用类模板,你可以传入某个具体类型作为模板实参;然后编译器将会基于该类型来实例化类模板。
  • 对于类模板而言,只有那些被调用的成员函数才会被实例化
  • 你可以用某种特定类型特化类模板
  • 你可以用某种类型局部特化类模板
  • 你可以为类模板的参数定义缺省值,这些值还可以引用之前的模板参数。
时间: 2024-09-11 10:50:30

类模板的相关文章

C++类模板的三种特化

说起C++的模板及模板特化,相信很多人都很熟悉,但是说到模板特化的几种类型,相信了解的人就不是很多.我这里归纳了模板特化的几种类型,一是特化为绝对类型:而是特化为引用,指针类型:三是特化为另外一个模板类. 这里用一个简单的例子来说明这三种情况: // general version template<class T> class Compare { public: static bool IsEqual(const T& lh, const T& rh) { return lh

C++技巧之二维动态数组类模板

C++提供了许多强大的机制来实现代码的高度复用.来使我们使用我们自已的类就像使用内置类型那样方便快捷.比如模板,运算符重载等等.模板好比如是一个大批量生产函数和类的工厂,使我们不用再去关心与数据类型相关的繁琐编程细节,把我们精力留给那些真正值得我们去认真思考的地方.而运算符重载则使我们的程序更直观更简洁,这不仅使我们更容易读懂我们的程序,而且使我们能以一种更为流畅的方式来表达我们的想法.就像上篇文章说到的,如果我们把动态分配的二维数组用类模板实现,并重载相应的操作符,我们就能十分方便的使用我们自

用auto_ptr类模板帮助动态内存管理

动态内存使用最多的是在C++应用程序的代码中.有过编程经验的程序员虽然都知道new操作符的使用一定要与delete匹配,在某些场合仍然可能有内存溢出.当异常被掷出时,程序的正常控制流程被改变,因此导致潜在的内存溢出.例如, void g() //可能掷出 { if (some_condition == false) throw X(); } void func() { string * pstr = new string; g(); //如果 g 掷出一个异常,内存溢出 delete pstr;

c++-C++问题,关于模板类和类模板,求大神帮助

问题描述 C++问题,关于模板类和类模板,求大神帮助 不知道错的原因,希望大神帮忙解决,不胜感激. #include<iostream>using namespace std;template <class T>class List{public: List(T *elem); void Traverse();private: typedef struct LNode { T *m_elem; struct LNode *next; }Node*LinkNode; LinkNode

ios-自定义类系统自带的类模板没有了、怎么办啊!

问题描述 自定义类系统自带的类模板没有了.怎么办啊! 每次新建继承系统的类的文件都是光秃秃的!太不方便了,原来新建都会自带类模板的啊?求解 解决方案 类模板多种类型的类模板自定义类模板,类模板的默认类型数组的模板实现,友元和类模板,友元函数,类模板与静态变量,类模板与普通类之间互相继承,类模板作为模板参数,类嵌套,类模板嵌套,类包装器 解决方案二: 楼主指的是什么环境什么编辑器 解决方案三: 我觉得你是不是直接选了Objective-C文件,而不是cocoa touch class

class-编写一个使用类模板对数组进行排序、查找和求元素和的程序。

问题描述 编写一个使用类模板对数组进行排序.查找和求元素和的程序. 设计一个类模板templateclass Array,用于对T类型的数组进行排序.查找和求元素和,然后由此产生模板类Array和Array. 解决方案 http://www.warting.com/program/201109/33601.html 第四题 解决方案二: 编写一个使用类模板对数组进行排序,查找和求元素和的程序.

C++类模板

在上篇文章(C++函数模板)中,主要介绍了C++中函数模板,与函数相似,类也可以被一种或多种类型参数化.容器类就是一个具有这种特性的典型的例子, 本文地址:http://www.cnblogs.com/archimedes/p/cpp-class-template.html,转载请注明源地址. 以下通过设计一个类模板Stack的实现来说明: 类模板Stack的实现 #include<iostream> #include<vector> #include<stdexcept&g

【C/C++学院】(11)泛型编程/函数模板/类模板

1.泛型编程基础 #include "iostream" using namespace std; void swap(int &a, int &b) { int c; c = a; a = b; b = c; } void swap(float &a, float &b) { float c; c = a; a = b; b = c; } void main() { int a = 1, b = 2; swap(a, b); float a1 = 1,

类模板使用时遇到undefined reference to的错误

问题描述 类模板使用时遇到undefined reference to的错误 #include using namespace std; const int Max=20; template class stack { private: T s[Max]; int index; public: int getindex() { return index; } void push(T a) { s[index]=a; index ++; } T pop(){ return s[index--];

编程-数组类模板Array,类中包括对数组进行排序、查找和求元素和 然后由此产生模板类Array&amp;amp;lt;Box&amp;amp;gt;

问题描述 数组类模板Array,类中包括对数组进行排序.查找和求元素和 然后由此产生模板类Array<Box> #include using namespace std;class Box{private: int a b c;public: int V; Box(int chint kint g) { a = ch; b = k; c = g; V = a*b*c; } bool operator <(Box &one) { int temp = V - one.V; if (