普通算法题不太涉及的C++知识点(五):文件操作,模板
1. 文件操作:
C++对文件操作需要调用库函数:
#include <fstream>
文件类型分为文本文件和二进制文件。
1.1 读写文本文件:
#include <iostream>
#include <fstream>
using namespace std;
void test(){
ofstream ofs1;
ofs1.open("text1.txt", ios::out);
ofs1 << "hello" << endl;
ofs1 << "world" << endl;
ofs1.close();
ifstream ifs1;
ifs1.open("text1.txt", ios::in);
if(!ifs1.is_open()){
cout << "error" << endl;
return;
}
//读数据方法:
//第一种:
char buffer[1024] = {0};
while(ifs1 >> buffer){
cout << buffer << endl;
}
//第二种:
char buffer[1024] = {0};
while( ifs1.getline(buffer, sizeof(buffer)) ){
cout << buffer << endl;
}
//第三种:
string buffer;
while( getline(ifs1, buffer) ){
cout << buffer << endl;
}
ifs1.close();
}
int main(){
test();
return 0;
}
1.2 读写二进制文件:
#include <iostream>
#include <fstream>
using namespace std;
class person(){
public:
char m_name[64];
int m_age;
};
void test(){
ofstream ofs;
ofs.open("text.txt", ios::out|ios::binary);
person p = {"bob", 18};
ofs.write( (const char*)&p, sizeof(person) );
ofs.close();
ifstream ifs;
ifs.oepn("text.txt", ios::in|ios::binary);
if(!is_open()){
cout << "文件打开失败" << endl;
return;
}
person p;
ifs.read((char*)&p, sizeof(person));
ifs.close();
}
int main(){
test();
return 0;
}
2. 模板:
模板就是建立通用的模具,大大提高复用性。
2.1 函数模板:
建立一个通用函数,其函数返回值和形参列表可以不具体制定,用一个虚拟的类型来代表。
2.1.1 函数模板语法:
template<typename T>
函数声明或定义
eg:
//各种数据类型交换函数:
template<typename T>
void myswap(T &a, T &b){
T temp = a;
a = b;
b = temp;
}
void test(){
int a = 10;
int b = 20;
//使用模板的两种方法:
//1.自动类型推导:
myswap(a, b);
//2.显示指定类型:
myswap<int>(a, b);
}
2.1.2 函数模板案例——数组排序:
template<class T>
void myswap(T &a, T &b){
T temp = a;
a = b;
b = temp;
}
template<class T>
void mysort(T arr[], int length){
for(int i=0;i<length;i++){
int max = i;
for(int j=i+1;j<length;j++){
if(arr[max] < arr[j]){
max = j;
}
}
if(max!=i){
myswap(arr[max], arr[i]);
}
}
}
template<class T>
void myprint(T arr[], int length){
for(int i = 0;i<length;i++){
cout << arr[i] << " ";
}
cout << endl;
}
void test(){
char c[] = "qsefrgth";
int num = sizeof(c)/sizeof(char);
mysort(c, num);
myprint(c, num);
int n[] = {1,3,5,3,2,7,0};
int num2 = sizeof(n)/sizeof(int);
mysort(n, num2);
myprint(n, num2);
}
2.1.3 普通模板和函数模板的区别:
普通函数调用时可以发生自动类型转换;
函数模板调用时,如果使用自动类型推导,不会发生自动类型转换;
函数模板调用时,如果使用指定类型方式,会发生自动类型转换。
2.1.4 普通函数和函数模板的调用规则:
优先调用普通函数;
可以通过空模板参数列表强制调用函数模板;
函数模板也可以重载;
如果函数模板能产生更好的匹配,则优先调用函数模板。
void myprint(int a, int b){
cout << 1 << endl;
}
template<class T>
void myprint(T a, T b){
cout << 2 << endl;
}
void test(){
int a = 10;
int b = 20;
myprint(a, b); //输出1,优先调用普通函数
}
void myprint(int a, int b);
template<class T>
void myprint(T a, T b){
cout << 2 << endl;
}
void test(){
int a = 10;
int b = 20;
myprint(a, b); //报错,优先调用普通函数
}
void myprint(int a, int b){
cout << 1 << endl;
}
template<class T>
void myprint(T a, T b){
cout << 2 << endl;
}
void test(){
int a = 10;
int b = 20;
myprint<>(a, b); //输出2,通过空模板参数列表,强制调用函数模板
}
函数模板的重载:
template<class T>
void myprint(T a, T b){
cout << 1 << endl;
}
template<class T>
void myprint(T a, T b, T c){
cout << 2 << endl;
}
void test(){
int a = 10;
int b = 20;
myprint(a, b, 100); //输出2
}
函数模板产生更好的匹配:
void myprint(int a, int b){
cout << 1 << endl;
}
template<class T>
void myprint(T a, T b){
cout << 2 << endl;
}
void test(){
char c1 = 'a';
char c2 = 'b';
myprint(c1, c2); //输出2,因为函数模板产生了更好匹配
}
2.2 模板的局限性:
模板的通用性不是万能的。
使用模板具体化来处理自定义数据类型:
class person{
public:
person(string name, int age){
m_name = name;
m_age = age
}
string m_name;
int m_age;
};
template<class T>
bool compare(T &a, T &b){
if(a==b){
return true;
}
return false;
}
//利用具体化版本实现person对比
template<> bool compare(person &a, person &b){
if(a.m_name==b.m_name && a.m_age==b.m_age){
return true;
}
return false;
}
void test(){
int a = 10;
int b = 20;
cout << compare(a, b) << endl; //正常输出
person p1("Tom", 10);
person p2("Tom", 10);
cout << compare(p1, p2) << endl; //没有具体化版本时运行出错,有则正确输出
}
2.3 类模板:
2.3.1 类模板的基本语法:
建立一个通用类,类中成员的数据类型可以不具体指定。
template<class T>
类
eg:
template<class nameType, class ageType>
class person{
public:
person(nameType name, ageType age){
m_name = name;
m_age = age;
}
nameType m_name;
ageType m_age;
};
void test(){
person<string, int> p1("bob", 12);
}
2.3.2 类模板与函数模板的区别:
类模板没有自动类型推导的使用方法;
类模板在模板参数列表中可以有默认参数。
template<class nameType, class ageType = int>
class person{
public:
person(nameType name, ageType age){
m_name = name;
m_age = age;
}
nameType m_name;
ageType m_age;
};
void test(){
//person p1("bob", 12); //错误
person<string, int> p1("bob", 12); //正确
person<string> p2("bob", 12); //正确
}
2.3.3 类模板中成员函数创建时机:
普通类中的成员函数一开始就可以创建;
类模板中的成员函数在调用时才创建。
2.3.4 类模板对象做函数参数:
一共有三种传入方式:
指定传入的类型;(最常用)
参数模板化;
整个类模板化。
template<class T1, class T2>
class person{
public:
person(T1 name, T2 age){
m_name = name;
m_age = age;
}
void show(){
cout << m_name << " " << m_age << endl;
}
T1 m_name;
T2 m_age;
};
//指定传入类型:(最常用)
void print1(person<string, int> &p){
p.show();
}
//参数模板化:
template<class T1, class T2>
void print2(person<T1, T2> &p){
p.show();
}
//整个类模板化:
template<class T>
void print3(T &p){
p.show();
}
void test(){
person<string, int> p1("bob", 13);
print1(p1);
print2(p1);
print3(p1);
}
2.3.5 类模板与继承:
当子类继承的父类是一个类模板时,子类在声明的时候,要指定出父类中T的类型;
如果不指定,编译器无法给子类分配内存;
如果想灵活指出父类中T的类型,子类也需变为类模板。
template<class T>
class base{
T m;
};
class son : public base<int>{
};
template<class T1, class T2>
class son2 : public base<T1>{
T2 obj;
};
2.3.6 类模板成员函数类外实现:
template<class T1, class T2>
class person{
public:
person(T1 name, T2 age);
//{
// m_name = name;
// m_age = age;
//}
void show();
//{
// cout << m_name << endl;
// cout << m_age << endl;
//}
T1 m_name;
T2 m_age;
};
template<class T1, class T2>
person<T1, T2>::person(T1 name, T2 age){
m_name = name;
m_age = age;
}
template<class T1, class T2>
void person<T1, T2>::show(){
cout << m_name << endl;
cout << m_age << endl;
}
2.3.7 类模板分文件编写:
person.h
#include <iostream>
#include <string>
using namespace std;
template<class T1, class T2>
class person{
public:
person(T1 name, T2 age);
void show();
T1 m_name;
T2 m_age;
};
person.cpp
#include "person.h"
template<class T1, class T2>
person<T1, T2>::person(T1 name, T2 age){
m_name = name;
m_age = age;
}
template<class T1, class T2>
void person<T1, T2>::show(){
cout << m_name << endl;
cout << m_age << endl;
}
person.hpp
#include <iostream>
#include <string>
using namespace std;
template<class T1, class T2>
class person{
public:
person(T1 name, T2 age);
void show();
T1 m_name;
T2 m_age;
};
template<class T1, class T2>
person<T1, T2>::person(T1 name, T2 age){
m_name = name;
m_age = age;
}
template<class T1, class T2>
void person<T1, T2>::show(){
cout << m_name << endl;
cout << m_age << endl;
}
chengxu.cpp
//第一种方法,直接包含源文件:
//#include "person.cpp"
//第二种方法,将h和cpp内容写在一起,后缀写为hpp:
#include "person.hpp"
int main(){
......
}
2.3.8 类模板与友元:
类内实现:
template<class T1, class T2>
class person{
//全局函数类内实现:
friend void myprint(person<T1, T2> p){
cout << p.m_name << endl;
cout << p.m_age << endl;
}
public:
person(T1 name, T2 age){
m_name = name;
m_age = age;
}
private:
T1 m_name;
T2 m_age;
};
类外实现:
#include <iostream>
#include <string>
using namespace std;
template<class T1, class T2>
class person;
//类外实现:
template<class T1, class T2>
void myprint2(person<T1, T2> p){
cout << p.m_name << endl;
cout << p.m_age << endl;
}
template<class T1, class T2>
class person{
//全局函数类外实现:
friend void myprint2<>(person<T1, T2> p);
public:
person(T1 name, T2 age){
m_name = name;
m_age = age;
}
private:
T1 m_name;
T2 m_age;
};
int main()
{
person<string, int> p1("bob", 12);
myprint2(p1);
return 0;
}
2.3.9 类模板案例——数组类封装:
myarray.hpp
#include <iostream>
using namespace std;
template<class T>
class myarray{
public:
myarray(int capacity){
m_capacity = capacity;
m_size = 0;
paddress = new T[m_capacity];
}
maarray(const myarray &arr){
m_capacity = arr.m_capacity;
m_size = arr.m_size;
//浅拷贝:
//paddress = arr.paddress
//深拷贝:
paddress = new T[arr.m_capacity];
for(int i = 0;i<m_size;i++){
paddress[i] = arr.paddress[i];
}
}
~myarray(){
if(paddress!=NULL){
delete paddress;
paddress = NULL;
}
}
//尾插法
void push_back(const T &val){
//判断容量是否足够:
if(m_capacity==m_size){
cout << "insert error" << endl;
return;
}
paddress[m_size] = val;
m_size++;
}
//尾删法:
void pop_back(){
//让用户访问不到最后一个元素即可
if(m_size==0){
return;
}
m_size--;
}
//通过下标访问元素:
T& operator[](int index){
return paddress[index];
}
//返回数组容量:
int getcapacity(){
return m_capacity;
}
int getsize(){
return m_size;
}
//operator=防止浅拷贝:
myarray& operator=(const myarray& arr){
if(paddress!=NULL){
delete paddress;
paddress = NULL;
}
m_capacity = arr.m_capacity;
m_size = arr.m_size;
paddress = new T[arr.m_capacity];
for(int i = 0;i<m_size;i++){
paddress[i] = arr.paddress[i];
}
return *this;
}
private:
T *paddress;
int m_capacity;
int m_size;
};
main.cpp
#include "myarray.hpp"
void test(){
myarray<int> arr1(5);
for(int i = 0;i<5;i++){
arr1.push_back(i);
}
for(int i = 0;i<5;i++){
cout << arr1[i] << endl;
}
//cout << arr1.getsize() << endl;
//cout << arr1.getcapacity() << endl;
arr1.pop_back();
arr1.pop_back();
for(int i = 0;i<arr1.getsize();i++){
cout << arr1[i] << endl;
}
}
int main(){
test();
return 0;
}