1.7类模板分文件编写
有两种组织方式:
组织方式1.直接cpp型,仍然在.h头文件中写类的定义,在.cpp文件中写类的声明,然后直接包含cpp(不包含.h,因为模板类的函数只有在调用的时候才现产生,包含.h的话代码里面虽然有函数代码,但实际上并未产生函数,在cpp中才会产生,这是和普通函数不一样的地方)
组织方式2.hpp型,把类的定义和类的实现写到一个文件中,然后改成hpp文件,直接包含hpp文件就行(.hpp=.h+.cpp)
下面先看
1.包含.cpp型
"lei.h"头文件中的代码,类的定义
#pragma once //在使用预编译指令#include的时候,为了防止重复引用造成二义性
#include<iostream>
using namespace std;
//父类模板类
template<class T1, class T2>
class base {
public:
T1 t1;
T2 t2;
base(T1 t1, T2 t2);
void func();
};
"lei.cpp"的类的实现
#include"lei.h"
//构造函数的实现
template<class T1, class T2>
base<T1, T2>::base(T1 t1, T2 t2) {
cout << "i am base" << endl;
this->t1 = t1;
this->t2 = t2;
}
//成员函数的实现
//一定要先在前面加上一行声明模板的template<>
template<class t1, class t2>
void base<t1, t2>::func() {
cout << "i am func" << endl;
}
主函数
#include<iostream>
#include<string>
#include"lei.cpp"
using namespace std;
int main() {
base<int, string>b(100, "zz");
}
2. .hpp型
由下图可见,把类的定义和声明写在一块,然后使用.hpp文件
lei.hpp文件中模板类的定义和实现
#pragma once //在使用预编译指令#include的时候,为了防止重复引用造成二义性
#include<iostream>
using namespace std;
//父类模板类
template<class T1, class T2>
class base {
public:
T1 t1;
T2 t2;
base(T1 t1, T2 t2);
void func();
};
//构造函数的实现
template<class T1, class T2>
base<T1, T2>::base(T1 t1, T2 t2) {
cout << "i am base" << endl;
this->t1 = t1;
this->t2 = t2;
}
//成员函数的实现
//一定要先在前面加上一行声明模板的template<>
template<class t1, class t2>
void base<t1, t2>::func() {
cout << "i am func" << endl;
}
主函数中对模板类的调用
#include<iostream>
#include<string>
#include"lei.hpp" //可以看出,这里从.cpp变成了.hpp
using namespace std;
int main() {
base<int, string>b(100, "zz");
}
补充知识点:
#pragma once一般由编译器提供保证:同一个文件不会被包含多次。这里所说的”同一个文件”是指物理上的一个文件,而不是指内容相同的两个文件。无法对一个头文件中的一段代码作#pragma once声明,而只能针对文件。此方式不会出现宏名碰撞引发的奇怪问题,大型项目的编译速度也因此提供了一些。缺点是如果某个头文件有多份拷贝,此方法不能保证它们不被重复包含。在C/C++中,#pragma once是一个非标准但是被广泛支持的方式。
兼容性#pragma once方式产生于#ifndef之后。#ifndef方式受C/C++语言标准的支持,不受编译器的任何限制;而#pragma once方式有些编译器不支持(较老编译器不支持,如GCC 3.4版本之前不支持#pragmaonce),兼容性不够好。#ifndef可以针对一个文件中的部分代码,而#pragma once只能针对整个文件。相对而言,#ifndef更加灵活,兼容性好,#pragma once操作简单,效率高。
1.8 全局函数作为模板类的友元
学习目标:
- 掌握类模板配合友元函数的类内和类外实现
全局函数类内实现 - 直接在类内声明友元即可
全局函数类外实现 - 需要提前让编译器知道全局函数的存在
建议:建议全局函数做类内实现,用法简单,而且编译器可以直接识别,
要是类外实现还得想办法让编译器知道这个函数的存在,还得人为调整顺序,不仅麻烦也很容易乱。
1.类内实现法
template<class T1,class T2>
class person {
public:
T1 age;
T2 name;
person(T1 age, T2 name) {
this->age = age;
this->name = name;
}
//类内全局函数实现,从书写形式上看起来有点像我是我自己的朋友的意思,如果这里不写friend,那就是一个普通的类的成员函数
friend void print_person_in(person<T1, T2>&p) {
cout << "类内实现时: " << p.age << " " << p.name << endl;
}
};
int main() {
person<string, int>p("张瑞强", 100);
print_person_in(p);
}
2.类外实现法(相当麻烦,不推荐,了解就好)
//因为我下面的全局函数实现的过程中还需要person,所以我还得提前声明
template<class T1, class T2>
class person;
//还必须把这个全局函数放到模板类的前面,否则就会出现链接失败的错误。相当麻烦
template<class T1, class T2>
void print_person_out(person<T1, T2>& p) {
cout << "类外实现时: " << p.age << " " << p.name << endl;
}
template<class T1,class T2>
class person {
public:
T1 age;
T2 name;
person(T1 age, T2 name) {
this->age = age;
this->name = name;
}
};
int main() {
person<string, int>p("张瑞强", 100);
print_person_out(p);
}