tuple是类似pair的模板,不同的tuple类型的成员类型不相同,一个tuple可以有任意数量的成员。
每个确定的tuple类型的成员数量是固定的。
tuple适用于将一些数据组合成单一的对象,但又不想麻烦地定义一个新数据结构来表示的情况。
tuple类型及其伴随类型和函数都定义在tuple头文件中。
tuple<T1,T2,...,Tn> t; | t是一个tuple,成员数为n,所有成员都进行值初始化 |
tuple<T1,T2,...,Tn> t(v1,v2,...,vn) | t是一个tuple,每个成员用对应的初始值v来进行初始化 此构造函数是explicit的。(不能隐式构造) |
make_tuple(v1,v2,...,vn) | 返回一个用给定初始值初始化的tuple tuple的类型从初始值的类型推断 |
t1 == t2 t1 != t2 | 当两个tuple具有相同数量的成员其成员对应相等时, 两个tuple相等,否则两个tuple不等。 |
t1 relop t2 | relop是关系运算符,两个tuple必须具有相同数量的成员 且成员类型能进行比较。 |
get<i>(t) | 返回t的第i个数据成员的引用 |
tuple_size<tupletype>:value | 一个类模板,通过一个tuple类型来初始化,该模板具有 一个名为value的pubic static数据成员,类型为size_t, 表示给定tuple类型中成员的数量。 |
tuple_element<i,tupletype>::type | 类模板通过整型常量和一个tuple类型来初始化 type表示给定tuple类型中指定成员的类型 |
定义一个tuple时,需要指出每个成员的类型。
tuple<string,size_t,double> someval("constants",42,2.7);
由于tuple的构造函数是explicit的,我们必须使用直接初始化语法。
tuple<size_t,size_t> twod = {1,2}; //错误
tuple<size_t,size_t> twod(1,2); //正确
标准库中的make_tuple函数用来生成tuple对象。
auto item = make_tuple("matter",5);
由于tuple的成员都是未命名的,我们需要通过get的标准库函数模板来访问成员。
使用get,我们必须指定一个显式模板实参,它返回指定成员的引用。
auto book = get<0>(item); //返回item的第一个成员
尖括号中的值必须是一个整型常量表达式。
使用辅助类模板(tuple_size和tuple_element)来获取tuple成员的数量和类型。
typedef decltype(item) trans; //trans是item的类型
size_t sz = tuple_size<trans>::value; //返回3
tuple_element<1,trans>::type cnt = get<1>(item); //cnt是一个int
tuple_element所使用的索引是从0开始计数的。
由于tuple定义了<和==运算符,我们可以将tuple序列传递给算法,并且可以在无序容器中将tuple作为关键字类型。
tuple的一个常见用途是从一个函数返回多个值。
struct sales_data {};
typedef std::tuple<vector<vector<sales_data>>::size_type, vector<sales_data>::const_iterator, vector<sales_data>::const_iterator> matches;
//书店在files中的索引(整个销售记录的位置),两个指向书店vector中元素的迭代器(对于单个销售记录的遍历器)
bool compareisbn() //比较函数
{}
//查找给定书籍
vector<matches> findbook(const vector<vector<sales_data>>& files, const string& book)
{
vector<matches> ret; //保存给定书籍数据
for (auto it = files.cbegin(); it != files.cend(); ++it) //遍历file的门店数据
{
auto found = equal_range(it->cbegin(), it->cend(), book, compareisbn); //比较每个门店的销售书籍与book是否相同
if (found.first != found.second) //若有相同的书籍
ret.push_back(std::make_tuple(it - files.cbegin(), found.first, found.second)); //向ret插入数据
}
return ret;
}
void reportresults(istream& in, ostream& os, const vector<vector<sales_data>>& file)
{
string s;
while (in >> s)
{
auto trans = findbook(file, s);
if (trans.empty())
{
os << s << " not found in any stores" << endl;
continue;
}
for (const auto& store : trans)
os << "store " << std::get<0>(store) << endl;
}
}
bitset类使我们对于位运算的使用更加容易,并且能处理超过最长整型类型大小的位集合。
bitset类定义在头文件bitset中。
当我们定义一个bitset时,需要声明它包含多少个二进制位,大小必须是一个常量表达式。
bitset<32> bitvec(1U); //32位,低位为1,其他位为0
bitset中的二进制位是未命名的,且位置是从0开始,我们可以通过位置来访问它们。
bitset<n> b; | b有n位,每一位均为0 此构造函数是一个constexpr |
bitset<n> b(U); | b是unsigned long long值u的低n位的拷贝。 若n大于unsigned long long的大小,则b中超过的高位被置为0 此构造函数是一个constexpr |
bitset<n> b (s,pos,m,zero,one) | b是string s从位置pos开始m个字符的拷贝。 s只能包含字符zero或one,否则将抛出异常 pos默认为0,m默认为string::npos(string的结束位) |
bitset<n> b (cp,pos,m,zero,one) | 与上一个构造函数相同,但从cp指向的字符数组中拷贝字符。 |
当我们使用一个整型值来初始化bitset时,此值将被转换为unsigned long long类型并被当作位模式来处理。
我们可以从一个string或一个字符数组指针来初始化bitset,字符都直接表达位模式。
当我们使用字符串表示数时,字符串下标最小的字符对应bitset的高位。
bitset<32> bitvec("1100"); //2,3两位为1,剩余两位为0
string中下标最大的字符(最右字符)用来初始化bitset中的低位(下标为0的二进制位)。
我们不必使用整个string来作为bitset的初始值,可以只用一个子串作为初始值。
string str("1111111000011011");
bitset<32> bitvec(str,5,4); //从str[5]开始的四个二进制位,1100
bitset操作定义了多种检测或设置一个或多个二进制位的方法。
b.any() | b中是否存在置位的二进制位 |
b.all() | b中所有位都置位了吗 |
b.none() | b中不存在置位的二进制位吗 |
b.count() | b中置位的位数 |
b.size() | 返回b的位数(大小) |
b.test(pos) | 若pos位置的位是置位的,则返回true |
b.set(pos,v) b.set() | 将位置pos处的位设置为bool值v。v默认为true 若未传递实参,则将b中所有位置位 |
b.reset(pos) b.reset() | 将位置pos处的位复位或将b中所有位复位 |
b.flip(pos) b.flip() | 改变位置pos处的位的状态或改变b中每一位的状态 |
b.to_ulong() b.to_ullong() | 返回一个unsigned long或一个unsigned long long值,其位模式与b相同。 若b中位模式不能放入指定的结果类型,则抛出一个异常 |
b.to_string (zero,one) | 返回一个string,表示b中的位模式。zero和one用来表示b中的0或1 |
os << b is >> b | 将b中二进制位打印为字符1或0,打印到流os 从is读取字符存入b,当下一个字符不是1或0时,读取过程结束 |
前五个操作都不接受参数,返回整个bitset的状态,其他操作则改变bitset的状态。
只有当bitset的大小小于等于对应的大小时,我们才能使用to_ulong和to_ullong操作。
unsigned long ulong = bitvec.to_ulong();
若bitset中的值不能放入给定类型中,则操作会抛出一个overflow_error异常。
bitset的输入运算符直到读取的字符数达到对应的大小时,或遇到不是1或0的字符时,或遇到文件尾时,读取过程才停止。
正则表达式是一种描述字符序列的方法,正则表达式库定义在头文件regex中。
regex | 表示有一个正则表达式类 |
regex_match | 将一个字符序列与一个正则表达式匹配(整体格式匹配) |
regex_search | 寻找第一个与正则表达式匹配的子序列(子串格式匹配) |
regex_replace | 使用给定格式替换一个正则表达式 |
sregex_iterator | 迭代器适配器,调用regex_search来遍历一个string中所有匹配的子串 |
smatch | 容器类,保存在string中搜索的结果 |
ssub_match | string中匹配的子表达式的结果 |
(seq,m,r,mft) (seq,r,mft) | 在字符序列seq中查找regex对象r中的正则表达式。 mft是一个可选的标志。 m是一个match对象,用来保存匹配结果的相关细节。 m和seq必须具有兼容的类型。 |
首先定义一个string来保存希望查找的正则表达式。
string pattern("[^c]ei"); //查找不在字符c之后的字符串ei
pattern = "[[::alpha:]]*" + pattern + "[[::alpha:]]*";
//[[::alpha:]]* 表示将匹配零个或多个字母
模式[[::alpha:]]*匹配任意字母,符号+和*分别表示我们希望“一个或多个”或“零个或多个”匹配。
将正则表达式存入pattern中,用它来初始化一个regex对象。
regex r(pattern); //构造一个用于查找模式的regex
定义一个名为results的smatch对象和搜索文本text,results用于保存匹配位置的细节信息。
smatch results; //保存搜索结果
string test = "receipt freind theif receive"; //搜索文本
调用regex_search,若函数找到匹配子串,就返回true。
if(regex_search(text,results,r))
{ cout << results.str(); }
我们可以指定一些标志来影响或控制regex对象的处理过程。
regex r(re) regex r(re,f) | re表示一个正则表达式 f是指出对象如何处理的标志,默认值为ECMAScript |
r1 = re | 将r1中的正则表达式替换为re,re表示一个正则表达式。 |
r1.assign(re,f) | 与使用赋值运算符效果相同 f是对象如何处理的标志。 |
r.mark_count() r.flags() | r中子表达式的数目 返回r的标志集 |
icase | 在匹配过程中忽略大小写 |
nosubs | 不保存匹配的子表达式 |
optimize | 执行速度优先于构造速度 |
ECMAScript | 使用ECMA-262指定的语法 |
basic | 使用POSIX基本的正则表达式语法 |
extended | 使用POSIX扩展的正则表达式语法 |
awk | 使用POSIX版本的awk语言的语法 |
grep | 使用POSIX版本的grep的语法 |
egrep | 使用POSIX版本的egrep的语法 |
标志的使用形式:
regex r("[[:alnum:]] + \\.(cpp | cxx | cc)$",regex::icase);
正则表达式与C++一样,可以通过在字符之前放置一个反斜线来去掉其特殊含义。
一个正则表达式的语法是否正确是在运行时解析的。
若正则表达式存在错误,则在运行时标准库会抛出一个类型为regex_error的异常。
regex_error有一个what操作来描述发生了什么错误,一个code成员来返回某个错误类型对应的数值编码。
try{...}
catch(regex_error)
{ cout << e.what() << " " << e.code() << endl; }
error_collate | 无效的元素校对请求 |
error_ctype | 无效的字符类 |
error_escape | 无效的转义字符或无效的尾置转义 |
error_backref | 无效的向后引用 |
error_brack | 不匹配的方括号[] |
error_paren | 不匹配的小括号() |
error_brace | 不匹配的花括号{} |
error_badbrace | {}中无效的范围 |
error_range | 无效的字符范围 |
error_space | 内存不足,无法处理正则表达式 |
error_badrepeat | 重复字符之前没有有效的正则表达式 |
error_complexity | 要求的匹配过于复杂 |
error_stack | 栈空间不足,无法处理匹配 |
正则表达式的错误类型编号从0开始计算。
正则表达式的编译是一个非常慢的操作,应尽量避免创建不必要的regex。
正则表达式可以搜索多种类型的输入序列,但是要确保类型与输入序列类型匹配。
smatch results;
if(regex_search("myfile",results,r) //错误,输入序列为char*
{ cout << results.str() << endl; }
输入序列类型 | 使用正则表达式类 |
string | regex,smatch,ssub_match,sregex_iterator |
const char* | regex,cmatch,csub_match,cregex_iterator |
wstring | wregex,wsmatch,wssub_match,wsrexgex_iterator |
const wchar_t* | wregex,wcmatch,wcsub_match,wcregex_iterator |
regex迭代器是一种迭代器适配器,被绑定到一个输入序列和一个regex对象上。
srgex_iterator it (b,e,r) | 一个sregex_iterator,遍历迭代器b和e表示的string。 它调用sregex_search(b,e,r)将it定位到输入中第一个匹配的尾置 |
sregex_iterator end | sregex_iterator的尾后迭代器 |
*it it-> | 返回一个smatch对象的引用或一个指向smatch对象的指针 |
++it it++ | 从输入序列当前匹配尾置开始调用regex_search 前置版本返回递增后迭代器,后置版本返回旧值 |
it1 == it2 it1 != it2 | 若两个迭代器都是尾后迭代器,则它们相等 若两个非尾后迭代器是从相同的输入序列和regex对象构造,则它们相等 |
sregex_iterator的使用方式:
for(sergex_iterator it(file.begin(),file.end(),r),end_it; it != end_it; ++it)
{ cout << it->str() << endl; }
一个ssub_match对象有两个str和length的成员,分别返回匹配的string和该string的大小。
m.ready() | 若已经通过调用regex_search或regex_match设置了m,则返回true |
m.size() | 若匹配失败,则返回0 否则返回最近一次匹配的正则表达式中子表达式的数目 |
m.empty() | 若m.size() = 0,则返回true |
m.prefix() | 一个ssub_match对象,表示当前匹配之前的序列 |
m.suffix() | 一个ssub_match对象,表示当前匹配之后的部分 |
m.format(...) | 正则表达式的替换操作 |
m.length(n) | 第n个匹配的子表达式的大小 |
m.position(n) | 第n个子表达式距序列开始的距离 |
m.str(n) | 第n个子表达式匹配的string |
m.begin(),m.end() m.cbegin(),m.cend() | 表示m中sub_match元素范围的迭代器 cbegin和cend返回const_iterator |
正则表达式中的模式通常包含一个或多个子表达式,正则表达式语法通常用括号来表示。
regex r("([[:alnum:]]+)\\.(cpp|cxx|cc)$",regex::icase);
//([[:alnum:]]+),匹配一个或多个字符的序列
//(cpp|cxx|cc),匹配文件扩展名
我们可以通过位置来访问模式中每个子表达式。
第一个子匹配位置为0,表示整个模式对应的匹配,随后是每个子表达式对应的匹配。
//若文件名为foo.cpp
results.str(0) = foo.cpp;
results.str(1) = foo;
子表达式的一个常见用途是验证必须匹配特定格式的数据。
ECMAScript正则表达式语言的特点:
若一个子表达式是完整的匹配的一部分,则其对应的ssub_match对象的matched成员是true的。
matched | 一个public bool数据成员 指出此ssub_match是否匹配了 |
first second | public数据成员,指向匹配序列首元素和尾后位置 |
length() | 匹配的大小,若matched为false,则返回0 |
str() | 返回一个包含输入中匹配部分的string,若matched为false,则返回空string |
s = ssub | 将ssub_match对象ssub转化为string对象s 等价于s = ssub.str() |
代码实例:
//正则表达式
string phone = "(\\()?(\\d{3})(\\))?([-. ])?(\\d{3})";
regex r(phone); //regex对象,用于查找我们的模式
smatch m; //每个smatch对象会包含5个ssub_match对象
valid(m); //valid函数用来检查m的每个子表达式的格式内容是否完整
bool valid(const smatch& m)
{
if(m[1].matched) //若有左括号,则数字后应紧跟一个右括号
return m[3].matched;
else
return !m[3].matched;
}
正则表达式的regex_replace可以使用在我们想将找到的序列替换成另一个序列的情况。
m.format(dest,fmt,mft) m.format(fmt,mft) | 使用格式字符串fmt生成格式化输出, 匹配在m中,可选的match_flag_type标志在mft中。 迭代器dest指向目的位置。 |
regex_replace(dest,seq,r,fmt,mft) regex_replace(seq,r,fmt,mft) | 遍历seq,用regex_search查找与regex对象r匹配的子串 使用格式化字符串fmt和可选的mft标志来生成输出。 seq是被替换的string r是原来的regex对象 fmt是新的替换规则 |
代码实例:
string fmt = "$2.$5.$7"; //将号码格式改为ddd.ddd.dddd
regex r(phone); //用来查找模式的regex对象
string number = "(908) 555-1800"; //被替换的string
cout << regex_replace(number,r,fmt) << endl;
标准库定义了用来替换过程中控制匹配或格式的标志。
这些匹配标志都定义在regex_constants的命名空间中,而regex_constants则定义在std中。
using std::regex_constants::format_no_copy;
//在名字前同时加上两个命名空间的限定符
match_default | 等价于format_defalut |
match_not_bol | 不将首字符作为行首处理 |
match_not_eol | 不将尾字符作为行尾处理 |
match_not_bow | 不将首字符作为单词首处理 |
match_not_eow | 不将尾字符作为单词尾处理 |
match_any | 若存在多于一个匹配,则返回任意一个匹配 |
match_not_null | 不匹配任何空序列 |
match_continuous | 匹配必须从输入的首字符开始 |
match_prev_avail | 输入序列包含第一个匹配之前的内容 |
format_default | 用ECMAScript规则替换字符串 |
format_sed | 用POSIX sed规则替换字符串 |
format_no_copy | 不输出输入序列中未匹配的部分 |
format_first_only | 只替换子表达式的第一次出现 |
默认情况下,regex_replace输出整个输入序列,未与正则表达式匹配的部分会原样输出;匹配的部分按格式字符串指定的格式输出。
程序通常需要一个随机数源。
定义在头文件random中的随机数库通过随机数引擎类和随机数分布类来解决随机数问题。
一个引擎类可以生成unsigned随机数序列。
一个分布类使用一个引擎类生成指定类型的、在给定范围内的、服从特定概率分布的随机数。
C++程序不应该使用库函数rand,而应使用default_random_engine类和恰当的分布类对象。
随机数引擎是函数对象类,可以通过调用一个随机数引擎对象来生成原始随机数。
default_random_engine e; //生成随机无符号数
for(size_t i = 0; i < 10; i++)
{ cout << e() << endl; }
标准库定义了多个随机数引擎类,区别在于性能和随机性质量不同。
engine e; | 默认构造函数,使用该引擎类型默认的种子 |
engine e(s); | 使用整型值s作为种子 |
e.seed(s); | 使用种子s重置引擎的状态 |
e.min() e.max() | 此引擎可生成的最小值和最大值 |
engine::result_type | 此引擎生成的unsigned整型类型 |
e.discard(u) | 将引擎推进u步,u的类型为unsigned long long |
对于大多数场合,随机数引擎的输出是不能直接使用的,因此随机数引擎的随机数也称为原始随机数。
我们使用一个分布类型的对象得到在一个指定范围内的数。
uniform_int_distribution<unsigned> v(0,9); //在0~9之间的均匀分布的随机数
default_random_engine e;
for(size_t i =0; i < 10; i++)
{ cout << v(e) << endl; } //每个调用返回在指定方位内并服从均匀分布的值
分布类型是函数对象类,它接受一个随机数引擎作为参数,引擎参数生成随机数,并将其映射到指定的分布中。
注意:我们传递给分布对象的是引擎对象本身,而不是引擎对象的一个调用。
当我们说随机数发生器时,是指分布对象和引擎对象的组合。
随机数发生器的伪随机:对于一个给定的发生器,每次运行程序它都会返回相同的数值序列。
一个函数若定义了局部的随机数发生器,应将其定义为static的。
vector<unsigned> randvec()
{
static default_random_engine e;
static uniform_int_distribution<unsigned> u(0,9);
......
}
若我们希望每次运行程序都会生成不同的随机结果,可以通过提供一个种子来达到这一目的。
种子是一个数值,引擎可以利用它从序列中一个新位置重新开始生成随机数。
为引擎设置种子的方式:在创建引擎对象时提供种子,或调用引擎中的seed成员。
default_random_engine e1; //默认种子
default_random_engine e2(34333); //使用给定的种子值
e1.seed(3153); //调用seed设置一个新种子值
调用系统函数time,其返回从一个特定时刻到当前经过了多少秒,该函数定义在头文件ctime中。
uniform_real_distribution类型的对象,并让标准库来处理从随机整数到随机浮点数的映射。
Dist d; | 默认构造函数,使d准备好被使用 分布类型的构造函数时explicit的 |
d(e) | 用相同的e连续调用d的话,会根据d的分布式类型生成一个随机数序列 e是一个随机数引擎对象 |
d.min() d.max() | 返回d(e)能生成的最小值和最大值 |
d.reset() | 重建d的状态,使得随后对d的使用不依赖于d已经生成的值 |
分布类型都是模板,具有单一的模板类型参数,表示分布生成的随机数的类型。
每个分布模板都有一个默认模板实参,当我们想使用默认随机数类型时应加上空尖括号:
uniform_real_distribution<> u(0,1);
新标准库的另一个优势在于可以生成非均匀分布的随机数。
标准库中含有一个分布不接受模板参数,即bernoulli_distribution(伯努利分布)。
伯努利分布是一个普通类,它总是返回一个bool值,概率是一个常数,默认值为0.5;
由于引擎返回相同的随机数序列,所以我们必须在循环外声明引擎对象。
bernoulli_distribution允许我们调整先行一方的概率。
bernouli_distribution b(.55);
标准库定义了一组操纵符来修改流的格式状态。
一个操纵符是一个函数或是一个对象,会影响流的状态,并能用作输入或输出运算符的运算对象。
操纵符用于两大类输出控制:控制数值的输出形式以及控制补白的数量和位置。
当操纵符改变流的格式状态后,通常改变后的状态对所有后续IO都生效。
由于上述原因,通常最好在不再需要特殊格式时尽快将流恢复到默认状态。
我们可以使用操纵符hex、oct和dec将其改为十六进制、八进制和十进制。
cout << oct << 20 << endl;
cout << hex << 20 << end;
操纵符hex、oct和dec只影响整型运算对象,浮点值的表示形式不受影响。
当对流应用showbase操纵符时,会在输出结果中显示进制。
cout << showbase << hex << 20 << endl; //0x14
cout << noshowbase; //操纵符noshowbase恢复到cout的默认状态
我们可以通过使用uppercase操纵符来输出大写的字母。
默认情况下,浮点值按六位数字精度打印。
我们可以通过调用IO对象的precision成员或使用setprecision操纵符来改变精度。
操纵符setprecision和其他接受参数的操纵符都定义在头文件iomanip中。
cout << setpricision(5); //打印精度改为5
cout.pricision(12); //精度改为12
默认情况下,精度值指定的是数字的总位数,而在执行scientific等操作符后,精度值控制的是小数点后面的数字位数。
setfill(ch) | 用ch来填充空白 |
setprecision(n) | 将浮点精度设置为n |
setw(w) | 读或写值的宽度为w个字符 |
setbase(b) | 将整数输出为b进制 |
默认情况下,输入运算符会忽略空白符(空格符,制表符,换行符,回车符)。
标准库还提供了一组底层操作,支持未格式化IO,允许我们把一个流当作一个无解释的字符序列。
is.get(ch) | 从istream is读取下一个字符存入字符ch中,返回is |
os.put(ch) | 将字符ch输出到ostream os。返回os |
is.get() | 将is的下一个字节作为int返回 |
is.putback(ch) | 将字符ch放回is,返回is |
is.unget() | 将is向后移动一个字节,返回is |
is.peek() | 将下一个字节作为int返回,但不从流中删除它 |
一般情况下,在读取下一个值之前,标准库保证我们可以退回最多一个值。
返回int的函数将它们要返回的字符先转换为unsigned char,然后再将结果提升到int。
头文件cstdio定义了一个名为EOF的const,我们可以用它来检测从get返回的值是否为文件尾。
while((ch = cin.get()) != EOF)
cout.put(ch);
一些未格式化IO操作能够一次性处理大块数据,有效提升了数据处理速度。
is.get(sink,size,delim) | 从is中读取最多size个字节,并保存在字符数组中 字符数组的起始地址由sink给出 读取过程直至遇到字符delim或读取了size个字符或遇到文件尾时停止 若遇到了delim,则将其留在输入流中,不读取出来存入sink |
is.getline(sink,size,delim) | 与上述版本类似,但会读取并丢弃delim |
is.read(sink,size) | 读取最多size个字节,存入字符数组sink中,返回is |
is.gcount() | 返回上一个未格式化读取操作从is读取的字节数 |
os.write(source,size) | 将字符数组source中的size个字节写入os,返回os |
is.ignore(size,delim) | 读取并忽略size个字符,包括delim。ignore含有默认参数 |
各种流类型通常都支持对六种数据的随机访问。
标准库提供了一对函数:seek来定位到流中给定的位置,tell告诉我们当前的位置。
流的随机访问只适用于fstream和sstream类型。
为了支持随机访问,IO类型维护一个标记来确定下一个读写操作要在哪里进行。
tellg() tellp() | 返回一个输入流中(teellg)或输出流(tellp)中标记的当前位置 |
seekg(pos) seekp(pos) | 在一个输入流或输出流中将标记重定位到给定的地址 pos通常是前一个tellg或tellp返回的值 |
seekp(off,from) seekg(off,from) | 在一个输入流或输出流中将标记定位到from之前或之后off个字符 from可以是以下值之一 |
beg 偏移量相对于流开始位置 cur 偏移量相对于流当前位置 end 偏移量相对于流结尾位置 |
fstream和stringstream类型可以读写同一个流,在这些类型中,有单一的缓冲区,标记也只有一个,表示缓冲区中的当前位置。
由于只有单一的标记,因此只要我们在读写操作间切换,就必须进行seek操作来重新定位标记。
pos的类型是pos_type,表示一个文件位置,而off_type表示距当前位置的偏移量。
更多【c++-C++ primer 第十七章】相关视频教程:www.yxfzedu.com