欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

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

程序员文章站 2024-03-26 09:09:59
...

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
相关标签: mooc