C++文件操作:
头文件fstream
ofstream、ifstream
理解输出输入流的含义,输出给文件(写),文件输入流(读)
二进制读写方法:
写文件时需要将一切类型强转为 const char*,读文件时需要将被写的对象强转为char*
使用ifstream::read将内容读到对应对象
使用ofstream::write将对象写到文件
模板:
函数模板:
可以进行自动类型推导(调用时无需指明typename的类型),或显示指定类型
必须确定出数据类型才可以使用
模板声明只允许在全局、命名空间或类范围内进行
与普通函数的区别:能否发生隐式类型转换(在使用自动类型推导时无法转换)
与普通函数同名时:优先调用普通函数,除非普通函数实在不符合输入类型(一个符合的都没有)
例:
template<typename T>
void myPrint(T a, T b) {
cout << "template:";
cout << a + b << endl;;
}
void myPrint(int a, int b) {
cout << "normal:";
cout << a + b << endl;;
}
调用myPrint时
输入为(int,int)输出为normal
输入为(char,int) 输出为normal
输入为(char,char)时输出为template
模板函数具体化,在同名函数前增加 template<> 标识,指明调用模板函数
例: template<> void myPrint(struct a,struct b){}
类模板:
只有显示指定类型的使用方式,不会自动类型推导
类模板可以有默认参数,函数模板不可以
例:
template<typename a = string,typename b = int>
class Person{};
类模板中的成员函数在调用时才生成(所以编译时即便实际调用的类型没有这个成员函数也不会出错)
typeid.name() 一个好方法
继承类模板时,子类当然也要带上模板,否则无法灵活继承父类,除非你一定要指明父类的模板类型(相当于父类作为传参,当然要在函数中指明那个typename是啥,否则函数本身就是函数模板)
在类模板外定义函数的实现时:不管函数实现内容中是否需要模板,都要体现模板
类模板分文件编写时:联系类模板成员函数的生成时机,无法链接到具体实现
解决方法:(1)直接包含源文件,编译器可以看到函数实现
(2)将头文件和源文件内容合在一个文件里写,后缀名改为hpp(hpp是约定的名称,并不是强制)
原因:因为include某一文件就相当于把某一文件的代码整段搬到进行include操作的文件中。对于一般的类来说,编译的时候是直接使用函数的声明进行编译操作,而看见了声明之后,编译器就会告诉自己在链接的时候要去找函数的实现。但对于类模板来说,编译器在编译过后由于并没有打算去寻找该函数,自己也没有创建该函数(因为实际上编译器创建函数是根据我们在cpp文件中的实现去创建的,但由于分文件编译期间编译器并没有“看见”cpp文件中的实现,所以编译器没法创建我们需要的成员函数,在链接的时候自然也就以为我们没有对该函数进行实现了)
关于模板函数的事情是,它们实际上不是函数。它们是功能模板。因此,它们不能像正常函数一样在上下文之外编译。只有在模板参数已知的情况下才能编译它们。所以编译是在使用它们的文件调用函数时完成的。为了实现这一点,模板需要对调用它的文件可见
类模板实现友元函数:
类内实现:正常编写即可(建议使用)
类外实现,需要表明该函数为模板函数,否则无法配合类模板作为函数参数的情况,其次应该先定义成员函数才能声明友元函数,最后又因为要先定义包含该类的函数,所以必须再定义该函数前声明该类。
template<typename c1,typename c2>//3.使用Person前需声明该类
class Person;
template<typename c1,typename c2>//2.声明友元函数前必须先定义
void print(Person<c1,c2> p){
cout<< p.name << p.sex << endl;
}
template<typename c1,typename c2>
class Person{
public:
friend void print<>(Person<c1,c2> p); //1.加<>表明为模板函数
Person(c1 name,c2 sex){
...
}
}