今天编程时碰到一个问题,写了一个单例的类模板,之前程序一直能跑,但今天使用了其中一个函数时却报错。后续查错,发现是自己忘记写结束符号了。

这就引出我知识点的不足了——之前为啥能跑正常?错误一只存在,为啥总是能编译通过?类中的函数何时才被实例化?类何时实例化?实例化的类是不是所有的函数都同时被实例化?

整理和理解网上资料后获得如下总结:

1.在我们使用类模板时,只有当代码中使用了类模板的一个实例的名字,而且上下文环境要求必须存在类的定义时,这个类模板才被实例化。

  1.1声明一个类模板的指针和引用,不会引起类模板的实例化,因为没有必要知道该类的定义。

  1.2定义一个类类型的对象时需要该类的定义,因此类模板会被实例化。

  1.3在使用sizeof()时,它是计算对象的大小,编译器必须根据类型将其实例化出来,所以类模板被实例化.

  1.4new表达式要求类模板被实例化。

  1.5引用类模板的成员会导致类模板被编译器实例化。

  1.6需要注意的是,类模板的成员函数本身也是一个模板。标准C++要求这样的成员函数只有在被调用或者取地址的时候,才被实例化。用来实例化成员函数的类型,就是其成员函数要调用的那个类对象的类型

总结1很好的解释了为啥我的程序在调用特定的成员函数时才会报错。

不过上面的介绍还是比较笼统,还有很多的细节点需要注意到,从网上转载如下:转载链接:http://www.cnblogs.com/assemble8086/archive/2011/10/02/2198308.html

一、类模板定义及实例化

. 定义一个类模板:

1 template<class 模板参数表>2 3 class 类名{4 5 // 类定义......6 7 };

其中,template 是声明类模板的关键字,表示声明一个模板,模板参数可以是一个,也可以是多个,可以是类型参数 ,也可以是非类型参数。类型参数由关键字class或typename及其后面的标识符构成。非类型参数由一个普通参数构成,代表模板定义中的一个常量。

例:

1 template<class type,int width>2 3 //type为类型参数,width为非类型参数4 5 class Graphics;

注意:

(1)如果在全局域中声明了与模板参数同名的变量,则该变量被隐藏掉。

(2)模板参数名不能被当作类模板定义中类成员的名字。

(3)同一个模板参数名在模板参数表中只能出现一次。

(4)在不同的类模板或声明中,模板参数名可以被重复使用。

 1 typedef string type; 2  3 template<class type,int width> 4  5 class Graphics 6  7 { 8  9 type node;//node不是string类型10 11 typedef double type;//错误:成员名不能与模板参数type同名12 13 };14 15 template<class type,class type>//错误:重复使用名为type的参数16 17 class Rect;18 19 template<class type> //参数名”type”在不同模板间可以重复使用20 21 class Round;


(5)
在类模板的前向声明和定义中,模板参数的名字可以不同。

 1 // 所有三个 Image 声明都引用同一个类模板的声明 2  3 template <class T> class Image; 4  5 template <class U> class Image; 6  7 // 模板的真正定义 8  9 template <class Type>10 11 class Image { //模板定义中只能引用名字”Type”,不能引用名字”T”和”U” };


(6)
类模板参数可以有缺省实参,给参数提供缺省实参的顺序是先右后左。

1 template <class type, int size = 1024>2 3 class Image;4 5 template <class type=double, int size >6 7 class Image;


(7)
类模板名可以被用作一个类型指示符。当一个类模板名被用作另一个模板定义中的类型指示符时,必须指定完整的实参表

 1 template<class type> 2  3 class Graphics 4  5 { 6  7 Graphics *next;//在类模板自己的定义中不需指定完整模板参数表 8  9 };10 11 template <calss type>12 13 void show(Graphics<type> &g)14 15 {16 17 Graphics<type> *pg=&g;//必须指定完整的模板参数表18 19 }


2.
类模板实例化

定义:从通用的类模板定义中生成类的过程称为模板实例化。

例:Graphics<int> gi;

类模板什么时候会被实例化呢?

当使用了类模板实例的名字,并且上下文环境要求存在类的定义时。

对象类型是一个类模板实例,当对象被定义时。此点被称作类的实例化点

一个指针或引用指向一个类模板实例,当检查这个指针或引用所指的对象时。

例:

 1 template<class Type> 2  3 class Graphics{}; 4  5 void f1(Graphics<char>);// 仅是一个函数声明,不需实例化 6  7 class Rect  8  9 {10 11   Graphics<double>& rsd;// 声明一个类模板引用,不需实例化12 13   Graphics<int> si;// si是一个Graphics类型的对象,需要实例化类模板14 15 }16 17 int main(){18 19   Graphcis<char>* sc;// 仅声明一个类模板指针,不需实例化20 21   f1(*sc);//需要实例化,因为传递给函数f1的是一个Graphics<int>对象。22 23   int iobj=sizeof(Graphics<string>);//需要实例化,因为sizeof会计算Graphics<string>对象的大小,为了计算大小,编译器必须根据类模板定义产生该类型。24 25 }


3.
非类型参数的模板实参

要点

绑定给非类型参数的表达式必须是一个常量表达式。

从模板实参到非类型模板参数的类型之间允许进行一些转换。包括左值转换、限定修饰转换、提升、整值转换。

可以被用于非类型模板参数的模板实参的种类有一些限制。

例:

 1 Template<int* ptr> class Graphics{…….}; 2  3 Template<class Type,int size> class Rect{……..}; 4  5 const int size=1024; 6  7 Graphics<&size> bp1;//错误:从const int*->int*是错误的。 8  9 Graphics<0> bp2;//错误不能通过隐式转换把0转换成指针值10 11 const double db=3.1415;12 13 Rect<double,db> fa1;//错误:不能将const double转换成int.14 15 unsigned int fasize=255;16 17 Rect<String, fasize> fa2;//错误:非类型参数的实参必须是常量表达式,将unsigned改为const就正确。18 19 Int arr[10];20 21 Graphics<arr> gp;//正确

二、类模板的成员函数

要点:

类模板的成员函数可以在类模板的定义中定义(inline函数),也可以在类模板定义之外定义(此时成员函数定义前面必须加上template及模板参数)。

类模板成员函数本身也是一个模板,类模板被实例化时它并不自动被实例化,只有当它被调用或取地址,才被实例化。

 1 template<class type> 2  3 Class Graphics{ 4  5 Graphics(){…}//成员函数定义在类模板的定义中 6  7 void out(); 8  9 };10 11 template<class type>//成员函数定义在类模板定义之外12 13 void Graphics<type>::out(){…}

三、类模板的友元声明

类模板中可以有三种友元声明:

.非模板友元类或友元函数

 1 class Graphics{void out();}; 2  3 Template<class T> 4  5 Class Rect{ 6  7 friend class Graphics;//类Graphics、函数 8  9 friend void create();// create、 out是类模板10 11 friend void Graphics::out();// Rect所有实例的友元12 13 };

2、绑定的友元类模板或函数模板。

3、非绑定的友元类模板或函数模板。

第二种声明表示类模板的实例和它的友元之间是一种一对一的映射关系。

如图:

第三种声明表示类模板的实例和它的友元之间是一种一对多的映射关系。

如图:

例:绑定的友元模板

 1 template<class type> 2  3 void create(Graphics<type>); 4  5 template<class type> 6  7 class Graphics{ 8  9 friend void create<type>(Graphics<type>);10 11 };

例:非绑定的友元模板

1 template<class type>2 3 class Graphics{4 5 template<class T>6 7 friend void create(Graphics<T>);8 9 };


注意
当把非模板类或函数声明为类模板友元时,它们不必在全局域中被声明或定义,但将一个类的成员声明为类模板友元,该类必须已经被定义,另外在声明绑定的友元类模板或函数模板时,该模板也必须先声明。

例:

 1 template <class T> 2  3 class A { 4  5 private: 6  7 friend class B<T>; //错误:类B必须先声明 8  9 };10 11 template <class T>12 13 class B{};

四、类模板的静态数据成员、嵌套类型

.类模板的静态数据成员

要点:

静态数据成员的模板定义必须出现在类模板定义之外。

类模板静态数据成员本身就是一个模板,它的定义不会引起内存被分配,只有对其实例化才会分配内存。

当程序使用静态数据成员时,它被实例化,每个静态成员实例都与一个类模板实例相对应,静态成员的实例引用要通过一个类模板实例。

例:

 1 template<class type> 2  3 class Graphics{ 4  5 static Graphics *next; 6  7 static const type item; 8  9 };10 11 template<class type>12 13 Graphics<type> * Graphics<type>::next=0;14 15 template<class type>16 17 type Graphics<type>::item=NULL;18 19 //静态成员定义分为两部分:前一部分是类型,比如Graphics<type>*,后一部分是名称和值,比如Graphics<type>::next=0;

2.类模板的嵌套类型

要点

在类模板中允许再嵌入模板,因此类模板的嵌套类也是一个模板,它可以使用外围类模板的模板参数。

当外围类模板被实例化时,它不会自动被实例化,只有当上下文需要它的完整类类型时,它才会被实例化。

公有嵌套类型可以被用在类定义之外,这时它的名字前必须加上类模板实例的名字。

例:

 1 template<class type> 2  3 class Graphics{ 4  5 public: 6  7 template<class T> 8  9 class Rect{void out(type a,T b);};10 11 };12 13 Graphics<int>::Rect<double> node;14 15 //引用公有嵌套类型必须加上类模板实例名字

五、成员模板

定义:成员定义前加上template及模板参数表。

要点:

在一个类模板中定义一个成员模板,意味着该类模板的一个实例包含了可能无限多个嵌套类和无限多个成员函数.

只有当成员模板被使用时,它才被实例化.

成员模板可以定义在其外围类或类模板定义之外.

例:

 1 template<class type> 2  3 class Graphics<type>{ 4  5 public:template<class T> 6  7 class Rect{void out(type a,T b);};}; 8  9 template<class Gtype> template<class TT>10 11 void Graphics<Gtype>::Rect<TT>::out(Gtype a,TT b){}//成员模板被定义在类模板定义之外(要根上完整模板实参)12 13 Graphics<int>的实例可能包括下列嵌套类型:14 15 Graphics<int>::Rect<double>16 17 Graphics<int>::Rect<string>

注意:类模板参数不一定与类模板定义中指定的名字相同。

六、类模板的编译模式

1.包含编译模式

这种编译模式下,类模板的成员函数和静态成员的定义必须被包含在“要将它们实例化”的所有文件中,如果一个成员函数被定义在类模板定义之外,那么这些定义应该被放在含有该类模板定义的头文件中。

2.分离编译模式

这种模式下,类模板定义和其inline成员函数定义被放在头文件中,而非inline成员函数和静态数据成员被放在程序文本文件中。

例:

 1 //------Graphics.h--------- 2  3 export template<class type> 4  5 Class Graphics 6  7 {void Setup(const type &);}; 8  9 //-------Graphics.c------------10 11 #include “Graphics.h”12 13 Template <class type>14 15 Void Graphics<type>::Setup(const type &){…}16 17 //------user.c-----18 19 #include “Graphics.h”20 21 Void main()22 23 {Graphics<int> *pg=new Graphics<int>;24 25 Int ival=1;26 27 //Graphics<int>::Setup(const int &)的实例(下有注解)28 29 Pg->Setup(ival);30 31 }

Setup的成员定义在User.c中不可见,但在这个文件中仍可调用模板实例Graphics<int>::Setup(const int &)。为实现这一点,须将类模声明为可导出的:当它的成员函数实例或静态数据成员实例被使用时,编译器只要求模板的定义,它的声明方式是在关键字template前加关键字export

.显式实例声明

当使用包含编译模式时,类模板成员的定义被包含在使用其实例的所有程序文本文件中,何时何地编译器实例化类模板成员的定义,我们并不能精确地知晓,为解决这个问题,标准C++提供了显式实例声明:关键字template后面跟着关键字class以及类模板实例的名字。

例:

1 #include “Graphics.h”2 3 Template class Graphics<int>;//显式实例声明


显式实例化类模板时,它的所有成员也被显式实例化。

七、类模板的特化及部分特化

1.类模板的特化

先看下面的例子:

1 Template<class type>2 3 Class Graphics{4 5 Public:void out(type figure){…}};6 7 Class Rect{…};

如果模板实参是Rect类型,我们不希望使用类模板Graphics的通用成员函数定义,来实例化成员函数out(),我们希望专门定义Graphics<Rect>::out()实例,让它使用Rect里面的成员函数。

为此,我们可以通过一个显示特化定义,为类模板实例的一个成员提供一个特化定义。

格式:template<> 成员函数特化定义

下面为类模板实例Graphics<Rect>的成员函数out()定义了显式特化:

Template<> void Graphics<Rect>::out(Rect figure){…}

注意:

只有当通用类模板被声明后,它的显式特化才可以被定义。

若定义了一个类模板特化,则必须定义与这个特化相关的所有成员函数或静态数据成员,此时类模板特化的成员定义不能以符号template<>作为打头。(template<>被省略)

类模板不能够在某些文件中根据通用模板定义被实例化,而在其他文件中却针对同一组模板实参被特化。

2.类模板部分特化

如果模板有一个以上的模板参数,则有些人就可能希望为一个特定的模板实参或者一组模板实参特化类模板,而不是为所有的模板参数特化该类模板。即,希 望提供这样一个模板:它仍然是一个通用的模板,只不过某些模板参数已经被实际的类型或值取代。通过使用类模板部分特化,可以实现这一点。

例:

1 template<int hi,int wid>2 3 Class Graphics{…};4 5 Template<int hi>//类模板的部分特化6 7 Class Graphics<hi,90>{…};


格式:
template<模板参数表>

注意:

部分特化的模板参数表只列出模板实参仍然未知的那些参数。

类模板部分特化是被隐式实例化的。编译器选择“针对该实例而言最为特化的模板定义”进行实例化,当没有特化可被使用时,才使用通用模板定义。

例:Graphics<24,90> figure;

它即能从通用类模板定义被实例化,也能从部分特化的定义被实例化,但编译器选择的是部分特化来实例化模板。

类模板部分特化必须有它自己对成员函数、静态数据成员和嵌套类的定义。

八、名字空间和类模板

类模板定义也可以被放在名字空间中。例如:

 1 Namespace cplusplus_primer{ 2  3 Template<class type> 4  5 Class Graphics{…}; 6  7 Template<class type> 8  9 Type create()10 11 {…}12 13 }

当类模板名字Graphics被用在名字空间之外时,它必须被名字空间名cplusplus_primer限定修,或者通过一个using声明或指示符被引入。例如:

1 Void main()2 3 {4 5 using cplusplus_primer::Graphics;6 7 Graphics<int> *pg=new Graphics<int>;8 9 }

注意:在名字空间中声明类模板也会影响该类模板及其成员的特化和部分特化声明的方式,类模板或类模板成员的特化声明必须被声明在定义通用模板的名字空间中(可以在名字空间之外定义模板特化)。

一个关于队列的例子,下面将其代码整理如下:

  1 #include "iostream.h"  2   3 template <class Type> class QueueItem;  4   5 template <class Type>  6   7 class Queue {  8   9 public: 10  11 friend ostream& operator<<(ostream &os,const Queue<Type> &q); 12  13 Queue() : front( 0 ), back ( 0 ) { } 14  15 ~Queue(){} 16  17 void add( const Type & ); 18  19 bool is_empty() const 20  21 { 22  23 return front == 0; 24  25 } 26  27 Type remove(); 28  29 private: 30  31 QueueItem<Type> *front; 32  33 QueueItem<Type> *back; 34  35 }; 36  37 template <class Type> 38  39 class QueueItem 40  41 { 42  43 public: 44  45 QueueItem(Type val){item=val;next=0;} 46  47 friend class Queue<Type>; 48  49 friend ostream& operator<<(ostream &os,const Queue<Type> &q); 50  51 friend ostream& operator<<(ostream &os,const QueueItem<Type> &qi); 52  53   54  55 private: 56  57 Type item; 58  59 QueueItem *next; 60  61 }; 62  63 template <class Type> 64  65 void Queue<Type>::add(const Type &val) 66  67 { 68  69 QueueItem<Type> *pt =new QueueItem<Type>(val); 70  71 if ( is_empty() ) 72  73 front = back = pt; 74  75 else 76  77 { 78  79 back->next = pt; 80  81 back = pt; 82  83 } 84  85 } 86  87 template <class Type> 88  89 Type Queue<Type>::remove() 90  91 { 92  93 if ( is_empty() ) 94  95 { 96  97 cerr << "remove() on empty queue \n"; 98  99 exit(-1);100 101 }102 103 QueueItem<Type> *pt = front;104 105 front = front->next;106 107 Type retval = pt->item;108 109 delete pt;110 111 return retval;112 113 }114 115 template <class Type>116 117 ostream& operator<<(ostream &os, const Queue<Type> &q) //输出队列成员118 119 {120 121 os << "< ";122 123 QueueItem<Type> *p;124 125 for ( p = q.front; p; p = p->next )126 127 os << *p << “ ;//用到了Queue和QueueItem的私有成员,因此需将此运算符重128 129 //载函数声明为Queue和QueueItem的友元,书上没有将此函数声明为QueueItem130 131 os << “ >”;//的友元。132 133 return os;134 135 }136 137 template <class Type>138 139 ostream& operator<< ( ostream &os, const QueueItem<Type> &qi )140 141 {142 143 os << qi.item;//用到了QueueItem的私有成员,因此需将此运算符重载函数声明144 145 //为QueueItem的友元146 147 return os;148 149 }150 151 void main()152 153 {154 155 Queue<int> qi;156 157 cout << qi << endl;158 159 int ival;160 161 for ( ival = 0; ival < 10; ++ival )162 163 qi.add( ival );164 165 cout << qi << endl;166 167 int err_cnt = 0;168 169 for ( ival = 0; ival < 10; ++ival ) {170 171 int qval = qi.remove();172 173 if ( ival != qval ) err_cnt++;174 175 }176 177 cout << qi << endl;178 179 if ( !err_cnt )180 181 cout << "!! queue executed ok\n";182 183 else cout << “?? queue errors: " << err_cnt << endl;184 185 }

运行结果

转载于:https://www.cnblogs.com/live-in-city/p/3420577.html

C++ 模板何时被实例化相关推荐

  1. C++类模板实例化条件

    (我不想了解这个,可是考试要考.... 并不是每次使用模板类都会实例化一个类 声明一个类模板的指针和引用不会引起类模板的实例化 如果检查这个指针或引用的成员时时,类模板会实例化 定义一个对象的时候需要 ...

  2. C++模板之隐式实例化、显示实例化、隐式调用、显示调用和模板特化详解

    模板的实例化指函数模板(类模板)生成模板函数(模板类)的过程.对于函数模板而言,模板实例化之后,会生成一个真正的函数.而类模板经过实例化之后,只是完成了类的定义,模板类的成员函数需要到调用时才会被初始 ...

  3. 【C++基础学习】引起类模板被实例化情形总结

    在我们使用类模板时,只有当代码中使用了类模板的一个实例的名字,而且上下文环境要求必须存在类的定义时,这个类模板才被实例化.并不是每次使用一个类都要求知道该类的定义. (1)声明一个类模板的指针和引用, ...

  4. C++ 模板实例化与调用

    代码编译运行环境:VS2017+Debug+Win32 文章目录 1.隐式实例化 1.1 模板隐式实例化的定义 1.2 函数模板隐式实例化 1.3 类模板隐式实例化 2.显示实例化 2.1 模板显示实 ...

  5. [转]C++函数模板与模板函数

    1.函数模板的声明和模板函数的生成 1.1函数模板的声明 函数模板可以用来创建一个通用的函数,以支持多种不同的形参,避免重载函数的函数体重复设计.它的最大特点是把函数使用的数据类型作为参数. 函数模板 ...

  6. C++知识点59——类模板(4、类模板的模板参数是一个类模板)

    接上一篇文章https://blog.csdn.net/Master_Cui/article/details/111824152 七.类模板的模板参数是一个模板类 类模板的模板参数本身可以是一个类模板 ...

  7. C++ 模板偏特化-来自STL的思考

    之前学习STL时接触过一段时间的模板,模板是C++泛型编程编程的基础 STL从头到尾都是模板泛型编程,我觉得用的最巧妙的就是在traits萃取技巧时用到的模板偏特化 先简要回顾一下模板吧,模板主要分为 ...

  8. C++中的类模板详细讲述

    一.类模板定义及实例化 1. 定义一个类模板: 1 template<class 模板参数表> 2 3 class 类名{ 4 5 // 类定义...... 6 7 }: 其中,templ ...

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

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

最新文章

  1. 如何向亲戚们解释人工智能可以干啥?
  2. mongodb导入bson文件_Python爬虫进阶教程(七):MongoDB数据库
  3. 使用python画CDF
  4. 浪潮存储双活方案:新疆道路运输管理局的大数据应用不再是梦
  5. Webpack实战(五):轻松读懂Webpack如何分离样式文件
  6. php框架和不用框架_如何选择一个PHP框架
  7. hive The specified datastore driver (“com.mysql.jdbc.Driver“) was not found
  8. 【5分钟Paper】Fast强化学习和Slow强化学习
  9. 计算机组成原理——第七章
  10. 编译OpenJDK:make[1]: *** 没有规则可制作目标“openjdk8/jdk/src/share/classes/java/applet/AppletContext.java”,
  11. 2021年一季度口腔护理行业网络关注度分析报告
  12. conda 小tips
  13. 英特尔神经计算棒是什么?边缘计算又是什么?
  14. 1K2G保驾护航,无穷小真理放光芒
  15. Java网课基础笔记(7)19-07-19
  16. 【正点原子Linux连载】第四十四章 设备树下的LED驱动实验 -摘自【正点原子】I.MX6U嵌入式Linux驱动开发指南V1.0
  17. 如何学好编程?学习经验汇总
  18. 美的2021高频题汇总 | 备战春招,刷这30题就够了!
  19. 海康视频监控接入心得
  20. 不支持python数据类型_python不支持的数据类型有( )。_学小易找答案

热门文章

  1. 计算机辅助开始于计算机发展的第几个阶段,计算机辅助开始于计算机发展第几阶段...
  2. linux服务器重启ctrl,Linux禁止Ctrl+Alt+Del重启
  3. vue 文字转语音mp3_阿里云tts 将文字转换成语音
  4. java和python的优势_Java和Python哪个更好?解读Python对比其他语言的优势
  5. linux中eclipse不运行,linux下启动不了eclipse
  6. 手机检测归属地 java_手机号归属地离线查询- Java
  7. java量_Java 2. 量与常量
  8. python 爬取网页内容 snmp_python通过SNMP协议收集服务器监控信息
  9. easyexcel 工具类_问了个在阿里的同学,他们常用的15款开发者工具!
  10. PostgreSQL源码学习(1)--PG13代码结构