程序设计与算法(三)第七周 输入输出与模板

1 、输入输出流及相关的类

这里写图片描述

这里写图片描述
这里写图片描述
这里写图片描述

#include <iostream>
using namespace std;
int main(){
    int x, y;
    cin>>x>>y;
    //将标准输出重定向到test中
    freopen("/home/CLionProjects/4_28_mooc/test", "w", stdout)
    if(y==0)//除数为0则打印错误信息到屏幕
        cerr<<"error."<<endl;
    else
        cout<<x/y;
    return 0;
}
#include <iostream>
using namespace std;
int main()
{
    double f;int n;
    freopen("/home/CLionProjects/4_28_mooc/test", "r", stdin);
    cin>>f>>n;
    cout<<f<<n<<endl;
    return 0;
}

istream类的成员函数

istream & getline(char*buf, int bufSize);从输入流中读取bufSize-1个字符到缓冲区buf,或读到碰到”\n”为止。

istream &getline(char*buf, int bufSize, char delim);从输入流中读取bufSize-1个字符到缓冲区buf,或碰到delim字符为止。

两个函数逗号自动在buf中读入数据的结尾添加’\0’。’\n’或delim都不会被读入buf,单会被从输入流中取走。如果输入流中’\n’或delim之前的字符个数达到或超过了bufSize个,就导入读入失败,结果是,虽然本次读入已完成,但是之后的读入就会失败了。

可以用if(!cin.getline(…))判断输入是否结束

bool eof();判断输入流是否结束

int peek(); 返回下一个字符,但不从流中去掉

istream & putback(char c) 将字符ch放回输入流

istream & ignore(int nCount=1, int delim=EOF);从流中删掉最多nCount个字符,遇到EOF时结束

这里写图片描述

流操纵算子

整数流的基数:流操纵算子dec,oct,hex,setbase

浮点数的精度(precision, setprecision)

设置域宽(setw, width)

用户自定义

使用include< iomanip>

#include <iostream>
#include <iomanip>
using namespace std;
int main(){
    int n=10;
    cout<<n<<endl;
    cout<<hex<<n<<"\n";
    cout<<dec<<n<<"\n";
    cout<<oct<<n<<"\n";
    return 0;
}
10
a
10
12

控制浮点数精度的流操纵算子

precision, setprecision。非定点方式输出

precision是成员函数,其调用方式为cont.precision(5)

setprecision是流操纵算子,其调用方式是 cout<

他们功能相同。指定输出浮点数的有效位数(非定点方式输出时)指定输出浮点数的小数点后的有效位(定点方式输出时)

定点方式:小数点必须出现在各位数后面

#include <iostream>
#include <iomanip>
using namespace std;
int main() {
    double x = 1234567.89, y = 12.34567;
    int n = 1234567;
    int m = 12;
    cout << setprecision(6) << x << "\t" << y << "\t" << n << "\t" << m<<endl;
    //以小数点位置固定的方式输出
    cout << setiosflags(ios::fixed) << setprecision(6) << x << "\t" << y << "\t" << n << "\t" << m << endl;
    //非定点方式输出,保留6位有效数字
    cout<<resetiosflags(ios::fixed)<<n<<endl;
}
1.23457e+06 12.3457 1234567 12
1234567.890000  12.345670   1234567 12
1.23457e+06

设置域宽的流操纵算子

设置域宽(setw, width) 两者功能相同,一个是成员函数,另一个是流操纵算子,调用方式不同

cin>>setw(4)或者cin.setw(5) ; cout<

#include <iostream>
#include <iomanip>
using namespace std;
int main() {
    int w=4;
    char string[10];
    cin.width(5);
    //宽度设置有效性是一次性的,在每次读入和输出之前都要设置宽度
    while(cin>>string){
        cout.width(w++);
        cout<<string<<endl;
        cin.width(5);
    }
}
1234567890
1234
 5678
    90
#include <iostream>
#include <iomanip>
using namespace std;
int main()
{
    int n = 141;
    //十六进制,十进制、八进制
    cout<<"1) "<<hex<<n<<" "<<dec<<n<<" "<<oct<<n<<endl;
    double x=1234567.89, y = 12.34567;
    //保留5为有效数字
    cout<<"2) "<<setprecision(5)<<x<<" "<<y<<" "<<endl;
    //保留小数点后面5位
    cout<<"3) "<<fixed<<setprecision(5)<<x<<" "<<y<<" "<<endl;
    //科学计数法输出,保留小数点后5位
    cout<<"4) "<<scientific<<setprecision(5)<<x<<" "<<y<<endl;
    //非负数要显示正号, 输出宽度为12字符,宽度不足用+填补
    cout<<"5) "<<showpos<<fixed<<setw(12)<<setfill('+')<<12.1<<endl;
    //非负数不显示正号,输出宽度为12字符,宽度不足则用右边填充字符填充
    cout<<"6) "<<noshowpos<<setw(12)<<left<<12.1<<endl;
    //输出宽度为12字符,宽度不足用左边字符填充
    cout<<"7) "<<setw(12)<<right<<12.1<<endl;
    //宽度不足时,负号和数值分列左右,中间用填充字符填充
    cout<<"8) "<<setw(12)<<internal<<-12.1<<endl;
    cout<<"9) "<<12.1<<endl;
    return 0;
}
1) 8d 141 215
2) 1.2346e+06 12.346 
3) 1234567.89000 12.34567 
4) 1.23457e+06 1.23457e+01
5) ++++12.10000
6) 12.10000++++
7) ++++12.10000
8) -+++12.10000
9) 12.10000
#include <iostream>
#include <iomanip>
using namespace std;
ostream &tab(ostream &output){
    return output<<"\t";
}
int main()
{
    cout<<"aa"<<tab<<"bb"<<endl;
    return 0;
}
aa  bb

用户自定义流操纵算子

因为iostream里对<<进行了重载(成员函数)

ostream&operator<<(ostream&(*p)(ostream&)); 该函数内部会调用p所指向的函数,并且以 *this作为参数,hex, dex, oct, 都是函数

文件读写

#include <fstream>
#include <iostream>
using namespace std;
int main()
{
    ofstream outFile("clients.data", ios::out|ios::binary);
    ofstream fout;
    fout.open("test.out", ios::out|ios::binary);//可以为绝对路径或相对路径
    if(!fout){
        cout<<"File open error!"<<endl;
    }
}
/*
 clients.data:文件名
 ios::out:文件打开方式
 ios::out:输出到文件,删除原有内容
 ios::app:输出到文件,保留原有内容,总在尾部添加
 ios::binary:以二进制文件格式打开文件
 */
 */

文件的读写指针

对于输入文件,有一个读指针

对于输出文件,有一个写指针

对于输入输出文件,有一个读写指针

标识文件操作的当前位置,该指针在哪里,读写操作就在那里进行。

#include <fstream>
#include <iostream>
using namespace std;
int main()
{
    ofstream fout("/home/baobao/CLionProjects/4_28_mooc/test", ios::app);
    //  ifstream fin("/home/baobao/CLionProjects/4_28_mooc/test", ios::ate); // 打开文件,定位文件指针到文件尾
    long location = fout.tellp();//获得写指针的位置
    location = 10;
    fout.seekp(location);
    fout.seekp(location, ios::beg);//从头数偏移量
    fout.seekp(location, ios::cur);//从当前数,可以为负数
    fout.seekp(location, ios::end);//从尾部数location,这里是location是个负数

}

字符文件的读写

文件本身是流,流的成员函数和操作算子页同样使用于文件

#include <iostream>
#include <fstream>
#include <vector>
#include <algorithm>
using namespace std;
int main(){
    vector<int > v;
    ifstream srcFile("/home/baobao/CLionProjects/4_28_mooc/test");
    int x;
    while(srcFile>>x)
    {
        cout<<"there";
        cout<<x<<endl;
        v.push_back(x);
    }
    srcFile.close();
    ofstream desFile("/home/baobao/CLionProjects/4_28_mooc/test");
    sort(v.begin(), v.end());
    for (int i = 0; i < v.size(); ++i) {
        cout<<v[i]<<endl;
        desFile<<v[i]<<" ";
    }
    desFile.close();
    return 0;
}
// test内容:1 234 9 45 6 879

二进制文件的读写

二进制写文件

#include <iostream>
#include <fstream>
using namespace std;
struct Student{
    char name[20];
    int score;
};
int main()
{
    Student s;
    ofstream OutFile("/home/baobao/CLionProjects/4_28_mooc/student.dat", ios::out|ios::binary);
    while(cin>>s.name>>s.score)
    {
        OutFile.write((char *)&s, sizeof(s));
    }
    OutFile.close();
    return 0 ;
}

二进制读文件

#include <iostream>
#include <fstream>
using namespace std;
struct Student{
    char name[20];
    int score;
};
int main()
{
    Student s;
    ifstream inFile("/home/baobao/CLionProjects/4_28_mooc/student.dat", ios::in|ios::binary);
    if(!inFile){
        cout<<"error"<<endl;
        return 0;
    }
    while(inFile.read((char*)&s, sizeof(s))){
        int readedBytes = inFile.gcount();
        cout<<s.name<<" "<<s.score<<" "<<readedBytes<<endl;
    }
    inFile.close();
    return 0;
}

二进制文件的改

#include <iostream>
#include <fstream>
#include <cstring>
using namespace std;
struct Student{
    char name[20];
    int score;
};
int main()
{
    Student s;
    fstream iofile("/home/baobao/CLionProjects/4_28_mooc/student.dat", ios::in|ios::out|ios::binary);
    if(!iofile){
        cout<<"error"<<endl;
        return 0;
    }
    iofile.seekp(2*sizeof(s), ios::beg);//定位写指针到第三个记录
    iofile.write("Mike", strlen("Mike")+1);
    iofile.seekg(0, ios::beg);//定位读指针到文件开头
    while(iofile.read((char*)&s, sizeof(s)))
        cout<<s.name<<" "<<s.score<<endl;
    iofile.close();
    return 0;
}

二进制文件复制

#include <iostream>
#include <fstream>
using namespace std;
int main(int argc, char *argv[])
{
    if(argc != 3){
        cout<<"Fine mane missing"<<endl;
        return 0;
    }
    ifstream inFile(argv[1], ios::binary|ios::in);
    if(!inFile){
        cout<<"Source file open error."<<endl;
        return 0;
    }
    ofstream outFile(argv[2], ios::binary|ios::out);
    if(!outFile){
        cout<<"New file open error."<<endl;
        inFile.close();
        return 0;
    }
    char c;
    while(inFile.get(c))
        outFile.put(c);
    outFile.close();
    inFile.close();
    return 0;

}

linux 运行方式:

g++ main.cpp 这句话产生的是a.out

./a.out ./student.dat ./student1.dat

二进制文件和文本文件区别

  • Linux,Unix下的换行符号:’\n’ (ASCII码:0x0a)
  • Windows下的换行符号:’\r\n’ (ASCII码:0x0d0a) endl就是’\n’
  • MAC OS下的换行符合:’\r’ (ASCII码:0x0d)

导致Linux, Mac OS 文本文件在Windows记事本中打开时不换行

  • Unix/Linux下打开文件,用不用ios:binary没区别
  • Windows下打开文件,不用的话,则:
  • 读取文件时,所有的’\r\n’会被当作一个字符’\n’处理,即少读了一个字符’\r’
  • 写入文件时,写入单独的’\n’ 系统会自动在前面加一个’\r’,多写了一个’\r’

函数模板和类模板

交换两个数的模板

#include <iostream>
using namespace std;

template <class T>
void Swap(T &x, T &y)
{
    T tmp = x;
    x = y;
    y = tmp;
}

int main()
{
    int n=1, m=2;
    Swap(n, m);
    double f = 1.2, g=2.3;
    Swap(f,g);
    cout<<n<<" "<<m<<endl;
    cout<<f<<" "<<g<<endl;
    return 0;
}
//2 1
//2.3 1.2

求数组最大元素的MaxElement函数模板

#include <iostream>
using namespace std;

template <class T>
T MaxElement(T a[], int size){
    T tempMax = a[0];
    for (int i = 1; i < size; ++i) {
        if(tempMax<a[i]) tempMax = a[i];
    }
    return tempMax;
}
int main()
{
    int a[100];
    int n;
    cin>>n;
    for (int i = 0; i < n; ++i) {
        cin>>a[i];
    }
    int t = MaxElement(a, n);
    cout<<t;
    return 0;
}
//5
//2 3 4 2 3
//4
//不通过参数实例化函数模板
#include <iostream>
using namespace std;
template <class T>
T Inc(T n)
{
    return 1+n;
}
int main()
{
    cout<<Inc<double>(4)/2;
    return 0;
}
//2.5

函数模板的重载

函数模板可以重载,只要他们的形参表或类型参数标不同即可

//函数模板重载
#include <iostream>
using namespace std;
template <class T1, class T2>
void print(T1 arg1, T2 arg2){
    cout<<arg1<<" "<<arg2<<endl;
};
template <class T>
void print(T arg1, T arg2)
{
    cout<<arg1<<" "<<arg2<<endl;
}
template <class T, class T2>
void print(T arg1, T arg2)
{
    cout<<arg1<<" "<<arg2<<endl;
};

函数模板和函数的次序

在有多个函数和函数模板名字相同的情况下,编译器如下处理一条函数调用语句

  1. 先找参数完全匹配的普通(非由模板实例化而得的函数)
  2. 再找参数完全匹配的模板函数
  3. 再找实参经过自动类型转换后能够匹配的普通函数
  4. 上面都找不到,则报错
//函数模板重载
#include <iostream>
using namespace std;
template <class T>
T Max(T a, T b)
{
    cout<<"TemplateMax"<<endl;
    return 0;
}
template <class T, class T2>
T Max(T a, T2 b)
{
    cout<<"TemplateMax2"<<endl;
    return 0;
};
double Max(double a, double b){
    cout<<"MyMax"<<endl;
    return 0;
}
int main()
{
    int i=4, j=5;
    Max(1.2, 3.4);
    Max(i, j);
    Max(1.2, 3);//模板实例化
    return 0;
}

//MyMax
//TemplateMax
//TemplateMax2
#include <iostream>
using namespace std;
template <class T, class Pred>
void Map(T s, T e, T x, Pred op)
{
    for(; s!=e; ++s, ++x)
    {
        *x = op(*s);
    }
};
int Cube(int x){return x*x*x;}
double Square(double x){return x*x;}
int a[5] = {1,2,3,4,5}, b[5];
double d[5]={1.1,2.1,3.1,4.1,5.1}, c[5];
int main()
{
    Map(a, a+5, b, Square);
    //上面的实例化出了下面的函数,那个Square被实例化成了函数指针
//    void Map(int *s, int *e, int *x, double (*op)(double)){
//        for(;s!=e;++s,++x){
//            *x=op(*s);
//        }
//    }
    for (int i = 0; i < 5; ++i) {
        cout<<b[i]<<",";
    }
    cout<<endl;
    Map(a, a+5, b, Cube);
    for (int i = 0; i < 5; ++i) {
        cout<<b[i]<<",";
    }
    cout<<endl;
    Map(d, d+5, c, Square);
    for (int i = 0; i < 5; ++i) {
        cout<<c[i]<<",";
    }
    cout<<endl;
    return 0;
}

//1,4,9,16,25,
//1,8,27,64,125,
//1.21,4.41,9.61,16.81,26.01,

类模板:为了多快好省的定义一批相似的类,可以定义类模板,然后有类模板生成不同的类

#include <iostream>
using namespace std;
template <class T1, class T2>
class Pair{
public:
    T1 key;
    T2 value;
    Pair(T1 k, T2 v):key(k), value(v){};
    bool operator<(const Pair<T1, T2>&p)const ;
};
template <class T1, class T2>
bool Pair<T1, T2>::operator<(const Pair<T1, T2> &p) const {
    return key<p.key;
}
int main()
{
    Pair<string, int> student("Tom", 19);
    //实例化一个类,这个类名是:Pair<string, int>
    cout<<student.key<<" "<<student.value;
    return 0;
}
// Tom 19

编译器由类模板生成类的过程叫做类模板的实例化。由类模板实例化得到的类,叫模板类

同一个类模板的两个模板类不兼容

函数模板作为类模板成员

#include <iostream>
using namespace std;
template <class T>
class A{
public:
    template <class T2>
    void Func(T2 t){cout<<t;}
};
int main(){
    A<int> a;
    a.Func('K');
    a.Func("hello");
    return 0;
}
//Khello

类模板与非类型参数

类模板的“<类型参数表>”中可以出现非类型参数:

template <class T, int size>
class CArray{
    T array[size];
public:
    void Print(){
        for (int i = 0; i < size; ++i) {
            cout<<array[i]<<endl;
        }
    }
};
//这里a2和a3属于不同的类
CArray<double, 40> a2;
CArray<int, 50> a3;

类模板与派生

类模板与继承

类模板从类模板派生

#include <iostream>
using namespace std;
template <class T1, class T2>
class A{
    T1 v1;
    T2 v2;
};
template <class T1, class T2>
class B:public A<T2, T1>
{
    T1 v3; T2 v4;
};
template <class T>
class C:public B<T, T>{
    T v5;
};
int main()
{
    B<int, double> obj1;
    C<int> obj2;
    return 0;
}

类模板从模板类派生

template <class T1, class T2>
class A{
    T1 v1;T2 v2;
};
template <class T>
class B:public A<int, double>{
    T v;
};
int main()
{
    B<char> obj1; //自动生成两个模板类 A<int, double>和B<char>
    return 0;
}

类模板从普通类派生

class A{
    int v1;
};
template <class T>
class B:public A{
    T v;
};
int main(){
    B<char> obj1;
    return 0;
}

普通类从模板类派生

template <class T>
class A{
    T v1;
    int n;
};
class B:public A<int>{
    double v;
};
int main()
{
    B obj1;
    return 0;
}

类模板与友元

  • 函数、类、类的成员函数作为类模板的友元
void Func1(){}
class A{};
class B{
public:
    void Func(){}
};
template <class T>
class Tmp1{
    friend void Func1();
    friend class A;
    friend void B::Func();
};
  • 函数模板作为类模板
#include <iostream>
#include <string>
using namespace std;
template <class T1, class T2>
class Pair{
private:
    T1 key;
    T2 value;
public:
    Pair(T1 k, T2 v):key(k),value(v){}
    bool operator<(const Pair<T1, T2> &p) const;
    template <class T3, class T4>
    friend ostream &operator<<(ostream &o, const Pair<T3, T4>&p);
};
template <class T1, class T2>
bool Pair<T1, T2>::operator<(const Pair<T1, T2> &p) const {
    return key<p.key;
}
template <class T1, class T2>
ostream &operator<<(ostream &o, const Pair<T1, T2> &p)
{
    o<<"("<<p.key<<","<<p.value<<")";
    return o;
};
int main()
{
    Pair<string, int> student("Tom", 29);
    Pair<int, double> obj(12, 3.14);
    cout<<student<<" "<<obj;
    return 0;
}


//(Tom,29) (12,3.14)
//任意从template<class T1, class T2> 
//ostream &operator<<(ostream& o, const Pair<T1, T2> &p>
//生成的函数都是pair模板类的友元
  • 函数模板作为类的友元
#include <iostream>
using namespace std;
class A{
    int v;
public:
    A(int n):v(n){}
    template <class T>
            friend void Print(const T &p);
};
template <class T>
void Print(const T&p)
{
    cout<<p.v;
}
int main()
{
    A a(4);
    Print(a);
    return 0;
}
//4
// 所有从template<class T>
//      void Print(const T&p)
//生成的函数,都成为A的友元
//但是自己写的函数void Print(int a){}
// 不会成为A的友元
  • 类模板作为类模板的友元
#include <iostream>
using namespace std;
template <class T>
class B{
    T v;
public:
    B(T n):v(n){}
    template <class T2>
            friend class A;
};
template <class T>
class A{
public:
    void Func(){
        B<int> o(10);
        cout<<o.v<<endl;
    }
};
int main()
{
    A<double >a;
    a.Func();
    return 0;
}
//10
//A<double>类,成了B<int>类的友元。
//任何从A模板实例化出来的类,都是任何B实例化出来的类的友元

类模板与静态成员变量

#include <iostream>
using namespace std;
template <class T>
class A{
private:
    static int count;
public:
    A(){count++; }
    ~A(){count--;}
    A(A&){count++;}
    static void PrintCount(){
        cout<<count<<endl;
    }
};
template <> int A<int>::count=0;
template <> int A<double>::count=0;
int main(){
    A<int> ia;
    A<double>da;
    ia.PrintCount();
    da.PrintCount();
    return 0;
}

//1
//1

猜你喜欢

转载自blog.csdn.net/abc15766228491/article/details/80332052
今日推荐