C++ 之 类模板的分离式编译

模版编译篇


目录: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;
}

时间: 2024-10-20 20:09:23

C++ 之 类模板的分离式编译的相关文章

类模板的分离编译

一直觉得模板类是特别神奇的东西,它可以构造出不同类型的对象,使代码更加的灵活.这个过程就是类模板的实例化. 我们使用类的模板写一个stack类: #include<assert.h> #include"Seqlist1.h" using namespace std; template<class T,template<class> class Container  = Seqlist> class Stack { public: void Push(

【C++】C++问题——类模板分离编译、函数对象、智能指针

C++类模板的分离编译 过去很多类模板都是整个类连同实现都放在一个头文件里,像STL库就是遵循这样的策略来实现类模板的.现在的标准正试图矫正这种局面. 在实现中又许多函数模板.这意味着每个函数都必须包含模板声明,并且在使用作用域操作符的时候,类的名称必须通过模板变量来实例化. 比如一个operator=的代码: template <typename Object> const MemoryCell <Object> & MemoryCell<Object>::o

c++ 模板 不能 分离编译

C++Template头文件和定义分开编译的问题 (1) // Foo.htemplate<typename T>class Foo{public:void f();}; // Foo.cpp#include <iostream>#include "Foo.h" template<typename T>void Foo<T>::f(){std::cout << "Foo<T>::f()/n";}

C++ 模板的编译 以及 类模板内部的实例化

在C++中,编译器在看到模板的定义的时候,并不立即产生代码,只有在看到用到模板时,比如调用了模板函数 或者 定义了类模板的 对象的时候,编译器才产生特定类型的代码. 一般而言,在调用函数的时候,只需要知道函数的声明即可: 在定义类的对象时,只需要知道类的定义,不需要成员函数的定义. 但是,这对于模板编译是不奏效的,模板要进行实例化,则必须能够访问定义模板的源代码,当调用函数模板以及类模板的成员函数 的时候,需要知道函数的定义. 标准C++对于模板的编译提供了两种策略: 相同之处:"将类定义以及函

【C/C++学院】0823-静态联合编译与动态联合编译/父类指针子类指针释放/虚函数/纯虚函数概念以及虚析构函数/抽象类与纯虚函数以及应用/虚函数原理/虚函数分层以及异质链表/类模板的概念以及应用

静态联合编译与动态联合编译 #include <iostream> #include <stdlib.h> //散列 void go(int num) { } void go(char *str) { } //class //::在一个类中 class A { public: void go(int num) { } void go(char *str) { } }; void main() { ///auto p = go;编译的阶段,静态联编 void(*p1)(char *s

C++ 类模板二(类模版与友元函数)

//类模版与友元函数 #include<iostream> using namespace std; template<typename T> class Complex{ public: Complex(T a,T b); void Print() const//const修饰的是this指针 { cout << this->Real << ":" <<this->Image<< endl; } /*

【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,

C++ Primer 学习笔记_81_模板与泛型编程 --类模板成员[续1]

模板与泛型编程 --类模板成员[续1] 二.非类型形参的模板实参 template <int hi,int wid> class Screen { public: Screen():screen(hi * wid,'#'), cursor(hi * wid),height(hi),width(wid) {} //.. private: std::string screen; std::string::size_type cursor; std::string::size_type height

函数模板与类模板

函数模板,顾名思义,是在生成函数时依照的模板. 有时,我们需要对不同的数据类型做同样的函数操作. 比如:分别对一个int类型数 和 一个double类型数求平方. 这时,虽然都是同样的求平方操作(函数体内代码一样),但是我们必须要编写两个不同的函数,因为处理int类型的函数的参数和返回值类型都应该是int,而处理double类型的函数的参数和返回值都应该是double. 如下:函数体内操作代码一样,设计为重载函数,用相同的函数名,但是参数类型和返回值类型却都不一样,函数需要定义两次. int S