陈国栋
2009/5/29
1. not1的入参谓词不能是函数指针的原因。入参谓词不能为函数指针的还有:not1、not2、bind1st、bind2nd、compose1、compose2。
not1是unary_negate类的封装,而unary_negate用到入参谓词的argument_type类型。
ptr_fun是pointer_to_unary_function类的封装(当然对于二元也有一个类似的重载), pointer_to_unary_function类派生自unary_function,它就提供了相关的类型typedef.
参考阅读:
not1不是STL中唯一有那些要求的组件。四个标准函数适配器(not1、not2、bind1st和bind2nd)都需要存在某些typedef,一些其他人写的非标准STL兼容的适配器(比如来自SGI和Boost的——参见条款50)也需要。提供这些必要的typedef的函数对象称为可适配的,而缺乏那些typedef的函数对象不可适配。可适配的比不可适配的函数对象可以用于更多的场景,所以只要能做到你就应该使你的函数对象可适配。
2. not1和not2不能统一为一个not的原因。它们的入参都只有一个,无法通过重载来匹配不同的not。
3. mem_fun1和mem_fun1_ref不再是C++标准,用mem_fun和mem_fun_ref即可。系统自动根据重载调用合适的函数。
4. 选择mem_fun处理一个参数的类成员函数作为例子解析类成员适配器工作原理。mem_fun产生一个带两个参数的binary类型的函数对象。它重载的operator ()的第一个参数是待处理的对象指针、第二个参数是真正的类成员的入参。
如果要使用绑定器绑定类成员入参,应该用bind2nd。
5. 仿函数的可配接性仿函数的可配接性是指仿函数能够与其它仿函数配接在一起实现新的功能,如不小于60,可以利用STL自带的not1<int>和less<int>配接而成:not1(bind2nd(less<int>(), 60))。
当然,也可以用bind2nd(greater_equal<int>(), 60)。
greater_equal<int>()中的()表示调用类greater_equal<int>的构造函数生成函数对象。
6. for_each的函数对象参数。不能直接用类成员函数,如&Shape::draw。
应该用mem_fun(&Shape::draw)。
因为for_each调用函数对象时按照普通函数的方式调用,但是成员函数需要通过一个对象去调用p->draw,因此直接用类成员函数不行,编译不过。
7. 标准库算法综述。1、 每个算法就是一个模板函数或者一组(重载的)模板函数。
2、 返回迭代器的算法都以序列结束作为失败标志。
算法总共有60个,使用者应该使用哪些自己熟悉、理解的算法。
8. VC2005使用equal_to需要包含<list>,GNU不需要。 9. 标准容器操作表。表操作:指在指定位置插入、删除。
10. 容器的元素需要满足的条件。1、 容器保存的是元素的拷贝,所以提供了完好的赋值运算符才能作为容器元素,否则只能用指针。
2、 关联容器默认以<比较元素大小,如果不用默认方式,需要指定排序比较操作。排序准则必须定义了一种严格的弱顺序。非形式的说,这就要求小于和等于都必须是传递的。即,对于顺序准则cmp有:
3、 容器中只用小于或者用户提供的小于运算来比较,不会用等于。如:这就是说multimap/map的等价的key并不一定完全相同。
4、 <utility>中提供了依据“<”和”==”扩展出的其它比较运算符。需要使用命名空间std::rel_ops才能访问。
11. map的下标访问的实质。m[k] 相当于 (*(m.insert(make_pair(k, V())).first)).second
所以m[“Zhangshan”] = 25 这样的赋值效率不算太高。
12. hash_map与map的比较。 13. STL文件命名。Stl_algo.h algo即algorithm的简写。
14. 迭代器的操作和类别。 15. 迭代器特征类。iterator_traits
特征类定义了迭代器相关的各个类型。通过它能够根据迭代器就得到了相关的类型(如difference_type等),这样简化了接口界面。
16. 为了支持迭代器的重载解析,为每一类迭代器封装了一个类。这些封装的类只用于编译时选择正确的重载函数,没有其它作用。
用到了标志的继承性,输入类型为BinIter或者ForIter也会选择InIter。
17. 迭代器自增最好用++it,而不是it++。String的npos为什么是const static呢?
18. 引用类型小结。 18.1.引用实际上是持有一个固定地址的指针,但呈现给用户的是一个变量/对象。 18.2.const &类型可以支持右值为常量、或者表达式的情况,这些情况下会产生一个临时变量(可能在栈上)(其它情况不会产生临时变量),把引用变量作为该临时变量的别名。Const &是否必然有一次临时变量生成的过程?(不是,只有表达式或者常量时才会生成临时变量。)
如:double d = 3.14; const double & ref = d; 那么 &ref和&d相等吗?(相等。)
// const &一般不产生临时对象
void test2(void)
{
double d = 3.14;
const double & ref = d;
assert(&d == &ref); // const & 一般不生成临时变量, 支持常量或者表达式才产生临时变量
}
18.3.转换为引用类型,只是把指针类型进行了转换,不改变指针指向的值,没有生成临时变量。// 引用参数传递过程中的强制类型转换
void test5()
{
int b = 20;
//test5_helper((short)b); // ERROR 初始化非const引用类型为一个临时的int变量.
test5_helper((short &)b); // OK, 运行也正确, 说明short &的类型转换不会生成临时变量,没改变指针指向的地址,只是把指针类型进行了转换
assert(b == 30);
}
把变量强制类型转换再赋值给引用,那么应用类型转换中地址也不会发生改变。
参见下面的例子:
对引用进行强制类型转换,是否会产生临时对象?(没有另外分配空间产生临时变量。)
如:double d = 3.14; int & a = (int &)d; a = 10; 那么d是否会发生变化?会。
a的地址和d的地址一样,但a当作int来解释。
// 强转再赋值给引用变量,只是解释的类型不同,但两者地址相同
void test1(void)
{
double d = 3.14;
assert(d == 3.14);
int & a = (int &)d; a = 10; // 强转之后指向d的地址,但按照int解释
assert((int *)&d == &a);
assert(d != 3.14);
}
既能读又能写的子串。
查找到lamb子串,在该位置写入fun。(替换为fun)
basefield表示一组关于进制的bit,下面这种设置方法相当于清除其它进制的bit,只设置前面一个参数指定的进制。
P559,挺好的一种控制格式方式。
19. istream处理文件输入。如果szLine提供的buffer不足以保存一行的数据,则没有读取完的部分,下一次调用getline时会读取到。
ifstream ifs("test.txt");
if (!ifs) cerr << "open file fail!\n";
char szLine[500];
ifs.getline(szLine, sizeof(szLine));
关于文本文件输入的说明:
1、 MapFile方式要小心文件中间有00出现,它被解析为字符串结束。
2、 一般来讲,ifstream的速度已经相当快,它操作非常简单方便,建议用它。
3、 如果文件处理性能非常关键,可以自己写一个处理大文件的MapFile和流操作的类。如果考虑用类似MapFile+isstream方式,可以自己写一个性能非常高的字符串处理类,比如:astring/ustring。
20. cin执行失败需要cin.clear()清除失败标记之后再读。如果某一次cin>>i执行时,输入数据的类型与i不匹配,则该数据不会存储到i中,也不会从流中删除,而且cin流状态会变成fail,如果后面要继续读出数据,则需要调用cin.clear()之后再读。
int main()
{
int i;
if (cin >> i)
{
cout << "OK: i = " << i << endl;
}
else
{
cout << "ERROR!" << endl;
cin.clear();// 【重要】清除输入标志,必须有这句话,否则输入字母a,引起cin>>i执行失败,那么后面每一次cin>>都会直接返回失败。
//cin.putback(i); // 如果输入字母,则不会保存在i中, 这句话不能要
char a;
cin >> a;
cout << "Input is char : " << a << endl;
}
cout << "i is : " << i << endl;
cin.get();
system("PAUSE");
return 0;
}
21. 输入重定向和ignore的使用。#include <iostream>
#include <fstream> // 不能是ifstream,否则后面提示ifstream不是有效类型
using namespace std;
int main () {
char first, last;
ifstream ifs("test_ignore.txt");
cin.rdbuf(ifs.rdbuf()); // 标准输入重定向到文件
cout << "Enter your first and last names: ";
first=cin.get();
cin.ignore(256,' '); // 第一个字符之后的所有输入忽略,直到空格出现
last=cin.get();
cout << "Your initials are " << first << last;
system("PAUSE");
return 0;
}
int main( )
{
using namespace std;
ofstream file( "rdbuf.txt" );
streambuf *x = cout.rdbuf( file.rdbuf( ) );
cout << "test" << endl; // Goes to file
cout.rdbuf(x);
cout << "test2" << endl;
}
22. Insert()的有效性保证。插入操作可能使得序列式容器(list除外)内存重新分配,使得原有的引用、迭代器全部失效。
关联容器和list不会受到影响,因为关联容器一般采用二叉树实现,list是链表,都是链表类型,所以不受影响。
Vector的insert解决方案有两种:
对于deque来说:
23. erase()的有效性保证。直接操作set的元素,会使得树结构被破坏,所以不能通过iterator直接操作set的元素。
修改元素的部分属性,可以正确工作。
===========================================================================
STL速查表是为了在实际应用中便于快速查找而制作.
它主要以SGI STL库实现为依据,在DevC++编译器下实践通过,后期再补充VC2005下的对比与注意事项。
大类
小类
名字
头文件
简述
描述与举例
容器
序列
compose1
序列适配器
compose2
关联容器
拟容器
新容器
函数对象
无参数函数对象f()(Generator)
vector<int> V(10);
generate(V.begin(), V.end(), rand);
一元函数f(x)(Unary Function)
二元函数f(x,y)(Binary Function),
谓词(Predicate)【返回bool的函数对象】
全局函数转函数对象(binder和compose要求入参必须为函数对象,需要转换)
ptr_fun
完整代码:
int Multiply(int val,int multiplier)
{
return val * multiplier;
}
int a[10]={0,1,2,3,4,5,6,7,8,9}, b[10];
// 每个元素都乘以5
transform(a,a+10,b,bind2nd(ptr_fun(Multiply),5));