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 分离编译模式
在此演示一般的编译实例,和对于 函数或类模版的编译实例。
以自定义类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;
}
以定义函数模版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;
}
C++ —— 类模板的分离式编译的更多相关文章
- 【转】为什么C++编译器不能支持对模板的分离式编译
出处:刘未鹏(pongba) http://blog.csdn.net/pongba) 首先,一个编译单元(translation unit)是指一个.cpp文件以及它所#include的所有.h ...
- 为什么C++编译器不能支持对模板的分离式编译
首先,一个编译单元(translation unit)是指一个.cpp文件以及它所#include的所有.h文件,.h文件里的代码将会被扩展到包含它的.cpp文件里,然后编译器编译该.cpp文件为一个 ...
- C++_进阶之函数模板_类模板
C++_进阶之函数模板_类模板 第一部分 前言 c++提供了函数模板(function template.)所谓函数模板,实际上是建立一个通用函数,其函数类型和形参类型不具体制定,用一个虚拟的类型来 ...
- c++ 模板 不能 分离编译
C++Template头文件和定义分开编译的问题 (1) // Foo.htemplate<typename T>class Foo{public:void f();}; // Foo.c ...
- C++ 模板的编译 以及 类模板内部的实例化
在C++中.编译器在看到模板的定义的时候.并不马上产生代码,仅仅有在看到用到模板时,比方调用了模板函数 或者 定义了类模板的 对象的时候.编译器才产生特定类型的代码. 一般而言,在调用函数的时候,仅仅 ...
- C++ 函数模板与类模板(使用 Qt 开发编译环境)
注意:本文中代码均使用 Qt 开发编译环境,如有疑问和建议欢迎随时留言. 模板是 C++ 支持参数化程序设计的工具,通过它可以实现参数多态性.所谓参数多态性,就是将程序所处理的对象的类型参数化,使得一 ...
- C++基础 (9) 第九天 编译器对模板类的二次编译 类模板 自定义数组类
1 昨日回顾 2 编译器对于模板的二次编译 写一个模板函数 然后进行调用 g++ template.cpp -o template // 汇编 g++ -S template.cpp –o templ ...
- C++中重定义的问题——问题的实质是声明和定义的关系以及分离式编译的原理
这里的问题实质是我们在头文件中直接定义全局变量或者函数,却分别在主函数和对应的cpp文件中包含了两次,于是在编译的时候这个变量或者函数被定义了两次,问题就出现了,因此,我们应该形成一种编码风格,即: ...
- C++STL - 类模板
类的成员变量,成员函数,成员类型,以及基类中如果包含参数化的类型,那么该类就是一个类模板 1.定义 template<typename 类型形参1, typename 类型形参2,...&g ...
随机推荐
- Mysql 中和同to_char 一样用法的函数
STR_TO_DATE() $sql = " SELECT "; $sql .= " m_img,m_content,STR_TO_DATE(m_time,\" ...
- [转]最详细的 HTTPS 科普扫盲帖
转载自http://www.codeceo.com/article/https-knowledge.html 为什么需要https HTTP是明文传输的,也就意味着,介于发送端.接收端中间的任意节点都 ...
- C#基础知识01(continue、break 和 return、ref 和 out)
break[跳出循环或者退出一个switch语句]由于它是用来退出循环或者switch语句的,所以只有当它出现在这些语句中时才是合法的. continue 语句和break语句相似,只是它不是退出一个 ...
- 【转】 iOS开发 剖析网易新闻标签栏视图切换(addChildViewController属性介绍)
原文:http://blog.csdn.net/hmt20130412/article/details/34523235 本来只是打算介绍一下addChildViewController这个方法的,正 ...
- SWT/RAP计算器
/** *2.测试 */ public class NofTest extends AbstractEntryPoint { Text text,text2; RemoteObje ...
- iOS开发,新手入门指导
在做了近两年wp,安卓开发之后,某一天突然决定投身iOS的开发之中. 因为一直用的mac,做wp开发的时候都用双系统,vs开久了,就会比较烫,这点让人不爽.后来更多地做安卓,直接mac下开发,很舒适的 ...
- arp断网攻击解决办法
局域网中有这个提示arp断网攻击是正常的,说明防火墙已经拦截了,是有人用P2P工具控制你的网速,或者是局域网有机器中病毒了也会有这样的提示,不过不用担心,今天给大家带来几个防止arp断网攻击的办法,希 ...
- centos账户的uid和gid
修改/etc/passwd和/etc/group文件的UID和GID为0,可以获得root权限,不过不推荐~ UID和GID Linux系统如何区别不同的用户呢?可以很自然地想到,使用不同的用户名应该 ...
- git config --global core.excludesfile配置gitignore全局文件
Linux中,这时把全局要忽略的文件列表.gitignore放当前用户根目录下: git config --global core.excludesfile '~/.gitignogtire' Win ...
- 获取win7时区所有信息
打开命令行工具: tzutil /l # 或者输入到文件中tzutil /l > data.txt # -*- utf-8 -*- """获取win7所有时区信息, ...