关闭

C++程序设计 - Week 7 文件操作和模板

标签: 文件操作函数模板类模板模板类重定向
377人阅读 评论(0) 收藏 举报
分类:

文件操作

顺序文件 — 一个有限字符构成的顺序字符流
C++标准库中: ifstream, ofstream和fstream共3个类用于文件操作 — 统称为文件流类

派生关系
ios–> istream–> ifstream
ios–> ostream–> ofstream
ios–> (istream ostream)–> iostream–> fstream

ios::out 输出到文件, 删除原有内容
ios::app 输出到文件, 保留原有内容, 总是在尾部添加
ios::binary 以二进制文件格式打开文件

#include <fstream>
ofstream outFile(“clients.dat”, ios::out|ios::binary);

ofstream fout;
fout.open( “test.out”, ios::out|ios::binary );

if(!fout) { 
    cerr << “File open error!”<<endl; 
}

ofstream fout(“a1.out”, ios::app);
long location = fout.tellp(); //取得写指针的位置
location = 10L;
fout.seekp(location); // 将写指针移动到第10个字节处
fout.seekp(location, ios::beg); //从头数location
fout.seekp(location, ios::cur); //从当前位置数location
fout.seekp(location, ios::end); //从尾部数location 

ifstream fin(“a1.in”,ios::in);
long location = fin.tellg(); //取得读指针的位置
location = 10L;
fin.seekg(location); //将读指针移动到第10个字节处
fin.seekg(location, ios::beg); //从头数location
fin.seekg(location, ios::cur); //从当前位置数location
fin.seekg(location, ios::end); //从尾部数location

location可以为负值

函数模板

泛型程序设计(Generic Programming),算法实现时不指定具体要操作的数据的类型。算法实现一遍,适用于多种数据结构。减少重复代码的编写

函数模板、类模板

template<class 类型参数1, class 类型参数2, … >
返回值类型 模板名 (形参表)
{
    函数体
}

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

函数模板可以重载, 只要它们的形参表不同即可

C++编译器遵循以下优先顺序:
Step 1: 先找参数完全匹配的普通函数(非由模板实例化而得的函数)
Step 2: 再找参数完全匹配的模板函数
Step 3: 再找实参经过自动类型转换后能够匹配的普通函数
Step 4: 上面的都找不到, 则报错

赋值兼容原则引起函数模板中类型参数的二义性(int、double),可以在函数模板中使用多个类型参数, 可以避免二义性。

template<class T>
T myFunction(T arg1, T arg2) {...}
myFunction(5, 8.4); // error: replace T with int or double?

类模板

template <类型参数表>
class 类模板名
{
    成员函数和成员变量
};

template <类型参数表>
返回值类型 类模板名<类型参数名列表>::成员函数名(参数表)
{
    ……
}

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
//Pair的成员函数 operator <
{ 
    return key < p.key; 
}

编译器由类模板生成类的过程叫类模板的实例化,编译器自动用具体的数据类型,替换类模板中的类型参数,生成模板类的代码。由类模板实例化得到的类叫模板类

函数模版作为类模板成员

#include <iostream>
using namespace std;
template <class T>
class A{
public:
    template<class T2>
    void Func(T2 t) { cout << t; } //成员函数模板
};

类模板的参数声明中可以包括非类型参数

template <class T, int elementsNumber>

非类型参数: 用来说明类模板中的属性
类型参数: 用来说明类模板中的属性类型, 成员操作的参数类型和返回值类型

template <class T, int size>
class CArray{
T array[size];
public:
    void Print( )
    {
        for(int i = 0; i < size; ++i)
        cout << array[i] << endl;
    }
};

类模板与继承

  • 类模板派生出类模板
  • 模板类 (即类模板中类型/非类型参数实例化后的类)派生出类模板
  • 普通类派生出类模板
  • 模板类派生出普通类

string类

string类是一个模板类, 它的定义如下:

typedef basic_string<char> string;

string s1("Hello"); // 一个参数的构造函数
string s2(8, ‘x’); // 两个参数的构造函数
string month = “March”;

string s; // 可以将字符赋值给string对象
s = ‘n’;

cin >> stringObject;
getline(cin, s);

用 ‘=’ 赋值

string s1("cat"), s2;
s2 = s1; 用 assign成员函数复制

用 assign成员函数复制

string s1("cat"), s3;
s3.assign(s1); 

用 assign成员函数部分复制

string s1("catpig"), s3;
s3.assign(s1, 1, 3);

for(int i=0; i<s1.length(); i++)
    cout << s1.at(i) << endl;

成员函数at会做范围检查, 如果超出范围, 会抛出out_of_range异常, 而下标运算符不做范围检查

s1 += s2;
s1.append(s2);
s2.append(s1, 3, s1.size());

用关系运算符比较string的大小 == , >, >=, <, <=, !=

用成员函数compare比较string的大小

s2 = s1.substr(4,5); // 子串
s1.find("lo");  // 如果找不到,返回string::npos(string中定义的静态常量)

string s1("hello world");
printf("%s\n", s1.c_str());
// s1.c_str() 返回传统的const char * 类型字符串
//且该字符串以 ‘\0’ 结尾

string s1("hello world");
const char * p1=s1.data();
for(int i=0; i<s1.length(); i++)
printf("%c",*(p1+i));
//s1.data() 返回一个char * 类型的字符串
//对s1 的修改可能会使p1出错。

成员函数还有很多

输入输出

cin cout cerr clog

cerr和clog的区别在于cerr不使用缓冲区,直接向显示器输出信息;而输出到clog中的信息先会被存放在缓冲区,缓冲区满或者刷新时才输出到屏幕。

freopen("test.txt","w",stdout); //将标准输出重定向到 test.txt文
if( y == 0 ) // 除数为0则在屏幕上输出错误信息(或调试信息)
    cerr << "error." << endl;
else
    cout << x /y ; //输出结果到test.txt
freopen(“t.txt”,“r”,stdin); //cin被改为从 t.txt中读取数据

如果是从文件输入,比如前面有

freopen(“some.txt”,”r”,stdin);

那么,读到文件尾部,输入流就算结束。

如果从键盘输入,则在单独一行输入Ctrl+Z代表输入流结束

while(cin>>x) // 强制类型转换的重载,bool

istream & getline(char * buf, int bufSize);
// 从输入流中读取bufSize-1个字符到缓冲区buf,或读到碰到‘\n’为止(哪个先到算哪个)。
istream & getline(char * buf, int bufSize,char delim);
// 从输入流中读取bufSize-1个字符到缓冲区buf,或读到碰到delim字符为止(哪个先到算哪个)。
bool eof(); 判断输入流是否结束
int peek(); 返回下一个字符,但不从流中去掉.
istream & putback(char c); 将字符ch放回输入流
istream & ignore( int nCount = 1, int delim = EOF );
// 从流中删掉最多nCount个字符,遇到EOF时结束。

流操纵算子(自学)

使用流操纵算子需要 #include

整数流的基数:流操纵算子dec, oct, hex, setbase
浮点数的精度(precision, setprecision)
设置域宽(setw, width)
用户自定义的流操纵算子

0
0

查看评论
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
    个人资料
    • 访问:71515次
    • 积分:1492
    • 等级:
    • 排名:千里之外
    • 原创:77篇
    • 转载:10篇
    • 译文:0篇
    • 评论:7条
    文章分类
    最新评论