《C++程序设计语言》笔记之六

来源:互联网 发布:重庆邮电大学人工智能 编辑:程序博客网 时间:2024/05/19 22:02
11. 迭代器和分配器


迭代器:
一个迭代器是一个纯的抽象,也就是说任何在行为上像迭代器的东西也就是迭代器。迭代器是指向序列元素的指针概念的一种抽象
最关键的属性:
—— "当前被指向的元素"(间接,用运算符*和->表示);
—— "指向下一个元素"(增量,用运算符++标识)
—— 相等(用运算符==表示);

迭代器和函数声明在名字空间std中,在<iterator>中找到
迭代器不是一个通用指针,相反,它只是指向数组的指针概念的一个抽象。检测一个迭代器是否指向一个元素的检测很方便,
只要将它与序列的结束进行比较。(而不是将它与某个空元素比较)。

迭代器类别

Input  <--
     | <-- Forward <-- Bidirectional <-- Random access
Output <-- 

插入器:

将所产生的输出通过迭代器放入一个容器,在迭代器所指位置之后的元素就会被覆盖。也意味着溢出或随之而来的存储器破坏。
void f(vector<int>& vi)
{
fill_n(vi.begin(), 200, 7);
}
在<iterator>中提供了三个迭代器模板类,专门处理上述问题。加了三个函数使得迭代器的使用更加方便。
template<class Cont> back_insert_iterator<Cont> back_inserter(Cont& c);
template<class Cont> front_insert_iterator<Cont> front_inserter(Cont& c);
template<class Cont, class Out> insert_iterator<Cont> inserter(Cont& c, Out p);
在每次写入时,插入器将使用push_back(),push_front(),或者insert()把元素插入序列,而不是覆盖已有元素。
void f(vector<int>& vi)
{
fill_n(back_inserter(vi.begin()), 200, 7);
}

反向迭代器:

注: 由于它与 迭代器的顺序正好相反,因此迭代器的++操作,要通过对iterator的 -- 操作来实现。


分配器:

allocator(分配器)被用于将算法和容器的实现隔离于物理存储的细节之外。一个分配器提供了一套分配与释放存储的标准方式。
以及一套用作指针类型和引用类型的标准名字。分配器也是一种纯粹的抽象,任何在行为上像分配器的类型都是分配器。

12. 串

到C风格字符串的转换:
可以用C风格字符串去初始化string,也可以用C风格字符串给string赋值,反过来,一个string中所有字符的一个副本放进数组

template <class Ch, clsss Tr=char_traits<Ch>, class A = allocator<Ch>> class basic_string{
public:
// ...
// 转换到C风格字符串
const Ch* c_str() const;
const Ch* data() const;
size_type copy(Ch* p, size_type n, size_type pos=0) const;
};

函数data()将string里的字符写进数组,并返回指向这个数组的指针,这个数组由string所拥有,用户不应该是图删除它。
在随后对串的非const操作之后,用户也不能再信赖这个数组中的值。
c_str()函数很像data(),只是它在最后加一个0,作为C风格字符串的结束。
将c_str()的内容拷贝到一个char数组中
char * c_string(const string& s)
{
char* p = new char[s.length()+1];// 注意:+1,用于存放/0
s.copy(p, string::npos);
p[s.length()] = 0;// 注意:加入字符串结束符
return p;
}


13. 流

输入流:
在输入流中,对于非char类型的内部类型,输入时自动略过空白字符(包括空格,制表符等),而在字符的操作中,
所有的内容都当作字符,空格则是当作空格字符输入。

get()函数用于读取一个字符,getline()用于读取一行字符,一直读到换行符为止
这两个函数对于空白符的处理与其他字符完全一样,对输入字符的意义没有任何假定。

函数istream::get(char&)将一个字符读进它的参数,例如:
int main()
{
char c;
while(cin.get(c)) cout.put(c);
}

s.get(p, n, term);将至多n-1个字符读入p[0]...p[n-2].这种调用将总在它放入缓冲区的那些字符最后放一个0.
p指向一个至少有n个字符的数组,term给定一个结束符。
void f()
{
char buf[100];
cin>>buf; // 可疑,早晚会溢出
cin.get(buf, 100, '\n'); // 安全
}
如果输入中遇到结束符,这个字符将作为第一个未读的字符留在流中。绝不要两次调用get()而忘记删除结束符。
void subtle_infinite_loop()
{
char buf[256];
while(cin)
{
cin.get(buf, 256);//读一行
cin << buf; // 打印一行,忘记从cin中删除'\n'
}
}
这种例子更多应该使用getline()而不是get(),getline()将会把结束符从istream中清除掉。

read(p,n)将最多n个字符读入p[0]...p[n-1]中,这个读入函数不依赖于结束符。也不在其目标的最后放结束符0.它能实际读入n个字符
也就是说它只是简单地读入n个字符,不会将读入的字符做成C风格的字符串。

ignore()函数向read一样读字符,但它不将读入的字符存放到任何地方,也即丢弃掉了。

流的联结:
basic_ios的函数tie()用于设置或解开在一个istream和一个ostream之间的联结:
考虑
string get_passwd()
{
string s;
cout<< "Password: ";
cin >> s;
//...
}
如何保证"Password:"能在读入操作之前出现在屏幕上,对于cout的输出有缓冲。如果cin和cout独立,那么在输出缓冲满之前,
"Password:" 将不会出现在屏幕之上。解决方法是:通过cin.tie(&cout)将cout联结于cin。
那么当需要从最终输入源获取新字符,已完成一个输入操作时,与之联结的ostream的缓冲将被刷新。


格式化:
在上述的例子中都是使用的非格式化的输出,也就是说按照默认规则将对象转化为字符序列。但程序员需要更细节的控制。
类basic_ios是basic_istream和basic_ostream的基类,所以格式控制是基于各个独立的流进行的

格式状态:
class ios_base{
public:
//...
// 格式标志名:
typedef implementation_define1 fmtflgs;
static const fmtflags
skips, // 输入时逃过空白
left, // 域调整,值后填充
right, // 值前填充
internal, // 在符号和值之间填充
boolalpha, // 用符号形式表示真假 true false
dec, // 整数的技术,以10为基础输出 十进制
hex, // 十六进制
oct, // 八进制
scientific, // 浮点数格式 d.ddddEdd
fixed, // 浮点数格式 d.ddddd
showbase, // 输出前缀,八进制加0,十六进制家0x
showpoint, // 打印尾随的0
showpos, // 正整数加显示的+符号
uppercase, // 用E和X而不是e和x
adjustfield, // 与域调整相关的标志组
basefield, // 与整数基数有关的标志组
floatfield, // 与浮点数输出有关的标志组
unitbuf, // 每次输出操作之后刷新

fmtflags flags() const;// 读标志
fmtflags flags(fmtflags f);// 设置标志

fmtflags setf(fmtflags f){ return flags(flags()|f)};// 添加标志
// 设置或清除mask的标志位
fmtflags setf(fmtflags f, fmtflags mask){ return flags((flags()&~mask))|(f&mask);}

void unsetf(fmtflags mask){ flags(flags() & ~mask);}// 清除标志
};

整数输出:
<iostream>采取的解决方法是提供一个setf()版本,,有第二个"伪参数",用于指明除了新值之外,还希望设置哪些选项。
cout.setf(ios_base::oct, ios_base::basefield);// 八进制
cout.setf(ios_base::hex, ios_base::basefield);// 十六进制

如果需要表现出数字所用的基数,可以设置showbase,在前面增加:
cout.setf(ios_base::showbase);

浮点数输出:
浮点数的输出通过格式和精度控制。
cout.setf(ios_base::scientific, ios_base::floatfield);// 采用科学格式
cout<< "scientific:\t" << 1234.56789 << "\n";

cout.setf(ios_base::fix, ios_base::floatfield);// 采用定点格式
cout<< "fix:\t" << 1234.56789 << "\n";

cout.setf(ios_base::fmtflags(0), ios_base::floatfield);// 恢复默认格式(一般格式)
cout<< "default:\t" << 1234.56789 << "\n";

默认的精度是6,可以有precision()方法进行设置。
class ios_base{
public:
//...
streamsize precision() const;// 取得精度
streamsize precision(streamsize n) const;// 设置精度,并取得原精度
};
precision()是对浮点数进行摄入而不是简单截断,并且其并不影响整数输出

输出域:
有时候需要填充一行的特定区域,我们需要指定雨的宽度,并指定要填充时所用的一个字符:
class ios_base{
public:
//...
streamsize width() const;// 取得域宽
streamsize width(streamsize wide);// 设置域宽
};
class basic_ios : public ios_base {
public:
// ...
Ch fill() const;// 取得填充字符
ch fill(Ch ch);// 设置填充字符
};

默认的填充字符是空格,默认的域宽是0.

进行域的调整:
cout.setf(ios_base::left, ios_base::adjustfield);// 居左
cout.setf(ios_base::right, ios_base::adjustfield);// 居右
cout.setf(ios_base::internal, ios_base::adjustfield);// 中间

文件流:
#include <fstream>
std::ifstream from(argv[1]);
std::ofstream to(argv[2]);

ios_base中定义了打开的不同模式:
static openmode app,// 附加
ate, // 打开并找到文件尾
binary, // 采用二进制模式I/O
in, // 为读而打开
out, // 为写而打开
trunc; // 将文件截断到0长度

例如:
ofstream mystream(name.c_str(), ios_base::app);// 附加模式打开
ofstream mystream(name.c_str(), ios_base::in | ios_base::out);// 输入输出模式

关闭文件流:
mystream.close();


14. 数值:
数字的限制值在<climits>和<limits.h>中可以找到。名字为CHAR_BIT和INT_MAX等。类似<cfloat>和
<float.h>里面描述浮点数性质宏。

标准数学函数:
头文件<cmath>和<math.h>中提供了常用数学函数。
abs fabs ceil floor sqrt pow cos sin tan acos asin atan atan2 
sinh cosh tanh exp log log10 modf frexp fmode ldexp

向量算术:
valarray为标准库提供的向量。其设计为了加速常用数值向量操作

与vector区别,valarray更注重数据操作效率,而非灵活性和通用性。

valarray及其辅助功能都是为了高速计算设计,valarray实现者能够使用自己所知道的任何优化技术。

复数算术:
在标准库中提供了一个complex模板,用于复数的运算。

通用数值算法:
在<numeric>里,标准库按照<algorithm>中非数值算法的风格提供了几个他你哦个用的数值算法:
accumulate() // 积累在一个序列上运算的结果 也即向量元素求和推广
inner_product()// 积累在两个序列上运算的结果 内积,或者叫做 点积
partial_sum() // 通过在序列上的运算产生序列 用于处理增量变化的概念
adjacent_difference()// 通过在序列上的运算产生序列 

随机数:
在<sstdlib>和<stdlib.h>中,标准库提供了一个简单的随机数生成:
#define RAND_MAX implementation_defined/*大的正整数*/

int rand(); // 在0 与 RAND_MAX间的伪随机数
void srang(unsigned int i);// 将随机数生成器种子置为i
原创粉丝点击