C++中模板类型(下)

C++中模板类型(下)

类模板的继承

类模板成员函数类内实现

在类模板的继承中,我们必须像如下程序一样,在子类中给予父类模板数据类型,使得当定义子类对象时,从父类继承过来的拿一部分可以给予确定的内存空间。

#include <iostream>  
#include <string>  
using namespace std;  
  
template<class T1>  
class Person  
{  
public:  
    T1 age;  
};  
  
template<class T1, class T2>  
class Student :public Person<T2>  
{  
public:  
    T1 name;  
    Student(T1 name, T2 age)  
    {  
        this->name = name;  
        this->age = age;  
    }  
    void ShowInf()  
    {  
        cout << this->name << "的姓名为" << this->age << endl;  
    }  
};  
  
int main()  
{  
    Student<string, int> Object("张三", 19);  
    Object.ShowInf();  
}  

在此例中,我们将子类定义为模板类,这样我们可以灵活的变化子类中所有变量的数据类型(自己本身的和从父类那里继承的)。

注意:当类模板碰到继承时,需要注意一下几点:

① 当子类继承的父类是一个类模板时,子类在声明的时候,要指定出父类中T的类型;

② 如果不指定,编译器无法给子类分配内存;

③ 如果想灵活指定出父类中T的类型,子类也需变为类模板。

类模板成员函数类外实现

#include <iostream>  
#include <string>  
using namespace std;  
  
template<class T1>  
class Person  
{  
public:  
    T1 age;  
};  
  
template<class T1, class T2>  
class Student :public Person<T2>  
{  
public:  
    T1 name;  
    Student(T1 name, T2 age)  
    {  
        this->name = name;  
        this->age = age;  
    }  
    void ShowInf();  
};  
  
template<class T1, class T2> // 成员函数类外实现  
void Student<T1, T2>::ShowInf()  
{  
    cout << this->name << "的姓名为" << this->age << endl;  
}  
  
int main()  
{  
    Student<string, int> Object("张三", 19);  
    Object.ShowInf();  
}  

我们注意到,这里在类外声明时,作用域变为了Student<T1, T2>,而且为了让编译器认识T1,T2是待定义的数据类型,即模板数据类型,我们要在前面添加模板标志“Template<class T1, class T2>”。

类模板嵌套友元函数(用全局函数实现友元)

错误示例

#include <iostream>  
#include <string>  
using namespace std;  
  
template<class T1>  
class Person  
{  
public:  
    T1 age;  
};  
  
template<class T1, class T2>  
class Student :public Person<T2>  
{  
public:  
    T1 name;  
    Student(T1 name, T2 age)  
    {  
        this->name = name;  
        this->age = age;  
    }  
    friend void ShowInf(Student<T1, T2> &p);  
};  
  
template<class T1, class T2> // 友元函数类外实现  
void ShowInf(Student<T1, T2> &p)  
{  
    cout << p.name << "的姓名为" << p.age << endl;  
}  
  
int main()  
{  
    Student<string, int> Object("张三", 19);  
    ShowInf(Object);  
}  

问题在于:友元函数的函数声明与函数定义的位置颠倒了。大多数人问:没颠倒呀?不就是声明在前,定义在后吗?而且不是我们只要有声明,不论定义在哪个位置都可以链接到函数体吗?

其实,这是由于模板的性质:只在调用时候函数体才会被定义,移入代码区。而且当你编程多了,你会发现:如果函数模板的声明预定义是分开编写的话,编译器只会在“程序开头->函数模板声明位置”之间寻找函数体实现,之后的区域编译器是不会寻找的。

那有有人问了:为什么模板类的成员函数类外实现可以根据类内的函数声明连接到函数体的实现呢?这是因为类本身的性质,类其中有一个虚函数表,其中记录着每个函数体的地址,当这个模板类一旦被实例化,这些函数的实现会通过函数地址立刻被找到。

解决方法

我们要理解一下函数声明与函数定义前后顺序对于程序的影响:

 

当声明在前时,我们的编译器会根据函数声明去“程序开头->函数模板声明位置”寻找函数定义的区域,但是如果函数定义在下面,那就很抱歉了,编译器找不到,会抛出一个错误。

如果像下面这样编写程序,编译器会根据函数模板的声明很成功的链接到函数体实现位置:

 

改正后的实现程序

#include <iostream>  
#include <string>  
using namespace std;  
  
template<class T1,class T2> class Student;  
  
template<class T1, class T2> // 友元函数类外实现  
void ShowInf(Student<T1, T2> &p)  
{  
    cout << p.name << "的姓名为" << p.age << endl;  
}  
  
template<class T1>  
class Person  
{  
public:  
    T1 age;  
};  
  
template<class T1, class T2>  
class Student :public Person<T2>  
{  
public:  
    T1 name;  
    Student(T1 name, T2 age)  
    {  
        this->name = name;  
        this->age = age;  
    }  
    friend void ShowInf<T1, T2>(Student<T1, T2> &p); // 参数是模板,那自然这个函数就是函数模板  
};  
  
int main()  
{  
    Student<string, int> Object("张三", 19);  
    ShowInf(Object);  
}  

类模板的分文件编写

声明.h和.cpp文件且在主文件中声明”#include”xxx.h””

Person1.h文件

#pragma once  
  
template<class T1, class T2>  
class Person1  
{  
public:  
    T1 age;  
    T2 name;  
    Person1(T1 age, T2 name);  
    void ShowInf();  
};  

Person1.cpp文件

#include "Person1.h"  
  
template<typename T1, typename T2>  
Person1<T1, T2>::Person1(T1 age, T2 name)  
{  
    this->age = age;  
    this->name = name;  
}  
  
template<class T1,class T2>  
void Person1 < T1, T2 > ::ShowInf()  
{  
    cout << this->name << "的年龄为" << this->age << endl;  
} 

 

Main.cpp

#include <iostream>  
#include <string>  
#include "Person1.h"  
using namespace std;  
  
int main()  
{  
    Person1<string, int> Object("张三", 16);  
    Object.ShowInf();  
}  

以上程序时错误的!,原因如下:

因为当我们调用“#include<Person1.h>”时,文件中有函数的声明,这些函数的函数体都是在调用时才会被定义,因此我们在调用预定义命令”#include”时,啥都还没定义啥都还没调用呢!因此此时通过这些声明找不到函数体,编译器就会报错,这些错误在编译时是不会报错的(编译阶段只是找出语法上的错误),只有在运行时才会报错(找不着函数体肯定报错,运行出的错误全是程序逻辑上的错误)。

直接调用.cpp文件

Person1.cpp文件

#include "Person1.h"  
#include <iostream>  
using namespace std;  
  
template<typename T1, typename T2>  
Person1<T1, T2>::Person1(T1 age, T2 name)  
{  
    this->age = age;  
    this->name = name;  
}  
  
template<class T1,class T2>  
void Person1 < T1, T2 > ::ShowInf()  
{  
    cout << this->name << "的年龄为" << this->age << endl;  
}  

Person1.h文件

#pragma once  
  
template<class T1, class T2>  
class Person1  
{  
public:  
    T1 age;  
    T2 name;  
    Person1(T1 age, T2 name);  
    void ShowInf();  
}; 

​​​​​​​ 

Main.cpp文件

#include <iostream>  
#include <string>  
#include "Person1.cpp"  
using namespace std;  
  
int main()  
{  
    Person1<string, int> Object("张三", 16);  
    Object.ShowInf();  
}  

模板类的声明与实现全部在.hpp或者.h文件中,然后调用.hpp文件

Person1.hpp/Person1.h文件

#pragma once  
  
#include <iostream>  
using namespace std;  
  
template<class T1, class T2>  
class Person1  
{  
public:  
    T1 age;  
    T2 name;  
    Person1(T1 age, T2 name);  
    void ShowInf();  
};  
  
template<typename T1, typename T2>  
Person1<T1, T2>::Person1(T1 age, T2 name)  
{  
    this->age = age;  
    this->name = name;  
}  
  
template<class T1, class T2>  
void Person1 < T1, T2 > ::ShowInf()  
{  
    cout << this->name << "的年龄为" << this->age << endl;  
}  

Main.cpp

#include <iostream>  
#include <string>  
#include "Person1.h" // #include "Person1.hpp"  
using namespace std;  
  
int main()  
{  
    Person1<string, int> Object("张三", 16);  
    Object.ShowInf();  
} 

​​​​​​​ 

我们分文件创建模板类时,一般用.hpp文件,将模板类的实现和声明一起放在.hpp文件中。

如下图所示:

 

猜你喜欢

转载自blog.csdn.net/weixin_45590473/article/details/109697369
今日推荐