模版编译篇
目录:1、对于C++中类模板的分离式编译的认识
2、具体的实例
1、对于C++中类模板的分离式编译的认识
为什么C++编译器不能支持对模板的分离式编译(博文链接)
主要内容:编译器编译的一般工作原理、对模版的分离式编译的特殊性(模版的特殊性)
链接: http://blog.csdn.net/pongba/article/details/19130
模版编译的特殊性
对程序进行编译时,对于函数调用,编译器只要求函数的原型在调用点是可见的,至于函数的定义是否存在不做检查(在对程序进行链接时才检查函数的定义)。类似的,对于对象声明,编译器只要求所属的类定义在声明点是可见的,至于各成员函数的定义是否存在则不进行检查。因此,为了提高程序的可读性和可维护性,我们通常将函数原型和类定义放在头文件(.h文件)中,而函数定义(包括类成员函数的定义)则放在源文件(.cpp文件,又称实现文件)中。
但是,模版编译则有所不同。从本质上说,模版并不是代码,而是指导编译器生成代码的指令,模版实例才是真正的程序代码。编译器看到模版定义的时候,不会立刻产生代码,只有在看到模版的使用(如调用函数模版、使用类模版定义对象或通过对象调用类模板的成员函数)时,才会进行实例化,使用特定的模版实例代码。而为了成功地进行实例化,编译器必须能够使用相应的函数定义,因此,模版编译要求模版的定义和实现采用特别的文件组织方式。
C++语言中定义了两种编译模式:包含编译模式(inclusion compilation model)和 分离编译模式(separate compilation model)。
......
——摘自《C++语言程序设计》(蒋爱军、刘红梅、王勇、梁小萍编著)P349 13.3.3 模版编译与类模板的实现
......
提示:分离编译模式实现起来比较困难,因此,所有的C++编译器都支持包含编译模式,而只有某些C++编译器支持分离编模式。程序员在编译使用自定义的模版的程序时,需要查阅编译器的用户指南,以确定自己所用的编译器支持那种编译模式。
——摘自《C++语言程序设计》(蒋爱军、刘红梅、王勇、梁小萍编著)P352 13.3.3 分离编译模式
2、编译实例演示
在此演示一般的编译实例,和对于 函数或类模版的编译实例。
(1)一般的编译:
以自定义类date为例,首先创建一个Console Application project工程文件(选用C++实现),向其中添加 date.h 和 date.cpp 两个文件,其中,date.h 存放 类的声明,而 date.cpp 对 date.h 中声明的类中的函数做具体的函数定义(函数实现)。
①date.h仅需要存放对类的声明就好。
//********************************************************************************** //Date.h //对类Date及其派生类Ndate 进行声明 //********************************************************************************** class Date { public: Date(int initYear=2014, int initMonth=1, int initDay=1); void set(int nYear,int nMonth, int nDay); int getYear()const; int getMonth()const; int getDay()const; void print()const; void increment(); void decrement(); private: int month; int year; int day; }; class Ndate: public Date { public: Ndate(int initYear=2014, int initMonth=10, int initDay=16); void n_increment(int n); void n_decrement(int n); };
②date.cpp 需要 include"date.h" 然后再对 date.h 中声明的类中的函数做具体的函数定义。(注意:实现文件包含头文件)
//********************************************************************************** //Date.cpp //对Date.h 中声明的函数进行定义 //********************************************************************************** #include "date.h" #include <iostream> #include <iomanip> using namespace std; int daysInMonth(int mo,int yr); Date::Date(int initYear, int initMonth, int initDay) { year=initYear; month=initMonth; day=initDay; } void Date::set( int nYear,int nMonth, int nDay) { year=nYear; month=nMonth; day=nDay; } int Date::getYear()const { return year; } int Date::getMonth()const { return month; } int Date::getDay()const { return day; } void Date::print()const { switch(month) { case 1: cout<<setw(9)<< "January"<<setw(3); break; case 2: cout<<setw(9)<< "Fabruary"<<setw(3); break; case 3: cout<<setw(9)<< "March"<<setw(3); break; case 4: cout<<setw(9)<< "April"<<setw(3); break; case 5: cout<<setw(9)<< "May"<<setw(3); break; case 6: cout<<setw(9)<< "June"<<setw(3); break; case 7: cout<<setw(9)<< "July"<<setw(3); break; case 8: cout<<setw(9)<< "August"<<setw(3); break; case 9: cout<<setw(9)<< "September"<<setw(3); break; case 10: cout<<setw(9)<< "October"<<setw(3); break; case 11: cout<<setw(9)<< "November"<<setw(3); break; case 12: cout<<setw(9)<< "December"<<setw(3); break; } cout<< day << setw(5)<<year<< endl; } void Date::increment() { day++; if(day > daysInMonth(month,year)) { day=1; month++; if( month>12) { month=1; year++; } } } void Date::decrement() { day--; if(day==0) { if(month==1) { day=31; month=12; year--; } else { month--; day= daysInMonth(month, year); } } } int daysInMonth(int mo,int yr) { switch (mo) { case 1: case 3: case 5: case 7: case 8: case 10: case 12: return 31; case 4: case 6: case 9: case 11: return 30; case 2: if((yr % 4== 0&&yr %100 !=0)||yr % 400 == 0) return 29; else return 28; } } Ndate::Ndate(int initYear, int initMonth, int initDay) { set(initYear,initMonth,initDay); } void Ndate::n_increment(int n) { for(int i=1; i<=n; i++) increment(); } void Ndate::n_decrement(int n) { for(int i=1; i<=n; i++) decrement(); }
③以创建project后自动生成的main.cpp 中的 main() 作为程序执行入口,使用date的派生类Ndate 的实例化对象。需要 include"date.h"。
//********************************************************************************** //main.cpp //实例化 date 并使用 //********************************************************************************** #include <iostream> #include "date.h" using namespace std; int main() { Ndate date1(2008,8,8); //1970,1,1 --> 14099 days -->2008,8,8 for(int i=1; i<=16360-14099; i++)//1970,1,1 --> 16360 days -->2014,10,17 { date1.increment(); } date1.print(); return 0; }
(2)对于 函数或类 模板的编译:
以定义函数模版M_swap为例(交换两个实参的值),首先创建一个Console Application project工程文件(选用C++实现),向其中添加 M_swap.h 和 M_swap.cpp 两个文件,其中,M_swap.h 存放 函数模版或类模版成员函数 的声明,而 M_swap.cpp 对 M_swap.h 中声明的 函数模版或类模版成员函数 做具体的定义(实现)。
①在M_swap.h存放对 函数模版或类模板成员函数 的声明及定义,或给出声明后用预处理指令#include包含实现文件。(注意:头文件中给出声明和定义 或 头文件包含实现文件)
//********************************************************************************** //M_swap.h //存放对 函数模版或类模版成员函数 的声明及定义 //********************************************************************************** #ifndef M_SWAP_H_INCLUDED #define M_SWAP_H_INCLUDED //M_SWAP_H_INCLUDED为头文件相应的头文件哨兵 //当一个程序中因多次使用到模版时 而多次包含头文件M_swap.h时 //有可能出现“重复定义”的错误 因此使用#ifndef 语句 template<class T> void M_swap(T &a,T &b); //为了成功地对模版进行实例化,编译器必须能够使用相应的函数定义 //故 需要在头文件中直接给出函数的具体定义 但这样一般会导致头文件 //较长,而且模版的定义和实现混合在一起,不利于维护 //可用另一种办法,那便是 #include "M_swap.cpp" //在头文件中用预处理指令#include包含实现文件 #endif // M_SWAP_H_INCLUDED
②M_swap.cpp 直接给出 声明的 函数模版或类模版成员函数 做具体的函数定义。
//********************************************************************************** //M_swap.cpp //给出 声明的 函数模版或类模版的成员函数 做具体的函数定义 //********************************************************************************** #include <iostream> using namespace std; template<class T> void M_swap(T &a,T &b) { T temp; temp=a; a=b; b=temp; cout<<"swap completed!>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>"<<endl; }
③以创建project后自动生成的main.cpp 中的 main() 作为程序执行入口。实例化M_swap,需要 include"M_swap.h"。
//********************************************************************************** //main.cpp //实例化 模版函数 M_swap 并交换两个实参的值 //********************************************************************************** #include <iostream> #include <string> #include "M_swap.h" using namespace std; class Box{ public: Box(double wi=1,double le=1,double he=1):w(wi),l(le),h(he) {cout<<"Box Constructor!\n";} ~Box() {cout<<"Destrctor of Box!\n";} double getWidth() const{return w;} double getLength()const{return l;} double getHeight()const{return h;} double getVolume()const{return w*l*h;} void setWidht(int wi) {w=wi;} void setLenght(int le){l=le;} void setHeight(int he){h=he;} private: double w; double l; double h; }; int main() { int i[2]={22 ,66 }; char c[2]={‘a‘,‘b‘}; string s[2]={string(7,‘n‘),string(6,‘v‘)}; Box b[2]={Box(12,12,12),Box(2,2,2)}; cout<<"--------------------------------------\n"; cout<<"First:\t"<<i[0]<<"\tSecond:\t"<<i[1]<<endl; cout<<"First:\t"<<c[0]<<"\tSecond:\t"<<c[1]<<endl; cout<<"First:\t"<<s[0]<<"\tSecond:\t"<<s[1]<<endl; cout<<"First:\t"<<b[0].getVolume()<<"\tSecond:\t"<<b[1].getVolume()<<endl<<endl; cout<<"--------------------------------------\n"; M_swap(i[0],i[1]); M_swap(c[0],c[1]); M_swap(s[0],s[1]); M_swap(b[0],b[1]); cout<<"--------------------------------------\n"; cout<<"First:\t"<<i[0]<<"\tSecond:\t"<<i[1]<<endl; cout<<"First:\t"<<c[0]<<"\tSecond:\t"<<c[1]<<endl; cout<<"First:\t"<<s[0]<<"\tSecond:\t"<<s[1]<<endl; cout<<"First:\t"<<b[0].getVolume()<<"\tSecond:\t"<<b[1].getVolume()<<endl<<endl; cout<<"--------------------------------------\n"; return 0; }