Effective STL开端

This is the first blog part about STL.
Effective STL介绍了怎样结合STL组件来在库的设计得到最大的好处。这样的信息允许你对简单、直接的问题开发简单、直接的解决方案,也帮助你对更复杂的问题设计优雅的方法。
STL是与迭代器合作的C++标准库的一部分,包含了标准容器(包括string),iostream库的一部分,迭代器、函数对象和算法。

STL基本术语:
1、vector、string、deque和list被称为标准序列容器。set、multiset、map和multimap被称为标准关联容器。
2、5种迭代器
输入迭代器是每个迭代位置只能被读一次的只读迭代器。 输出迭代器是每个迭代位置只能被写一次的只写迭代器。输入和输出迭代器被塑造为读和写输入和输出流(例如,文件)。因此并不奇怪输入和输出迭代器最通常的表现分别是istream_iterator和ostream_iterator。前向迭代器有输入和输出迭代器的能力,但是它们可以反复读或写一个位置。不支持后退操作。双向迭代器就像前向迭代器,加上了后退的迭代操作。随机访问迭代器可以做双向迭代器做的一切事情,但它们也提供“迭代器算术”,即,有一步向前或向后跳的能力。vector、string和deque都提供随机访问迭代器。
3、重载了函数调用操作符(即operator())的任何类叫做仿函数类。从这样的类建立的对象称为函数对象或仿函数。STL中大部分可以使用函数对象的地方也可以使用真函数。

#include <iostream>
#include <list>
#include <map>
//using namespace std;
int _tmain(int argc, _TCHAR* argv[])
{
    using std::map;
    using std::list;
    using std::string;
    using std::cout;
 list<int> lists;
 map<string,double> maps;
 string s = "sdwe";
 cout<<s.data();
    getchar();
    return 0;
}

最好不要使用using namespace std;这种写法,因为这会把std的函数声明全部暴漏出来,可能会与自己写的代码函数名冲突,结果就是覆盖了自己的代码功能。 推荐使用 using std::classname。这种方法。

  • Rule3:使容器里对象的拷贝操作轻量而正确
    容器里容纳了对象,但不是你给他们的那个对象。此外,当你从容器中获取一个对象时,你所得到的对象不是容器里的那个对象。取而代之的是,当你向容器中添加一个对象,进入容器的是你指定的对象的拷贝。拷进去,拷出来。这就是STL的方式。
    由于继承的存在,拷贝会导致分割。即如果你以基类对象建立一个容器,而你试图插入派生类对象,那么当对象(通过基类的拷贝构造函数)拷入容器的时候对象的派生部分会被删除。
    如:
    vector< Widget > vw;
    class SpecialWidget;
    public Widget{};
    SpecialWidget sw;
    vw.push_back(sw);//sw被当做基类对象拷入vw,当拷贝时他的特殊部分丢失了。
    分割问题暗示了把一个派生类对象插入基类对象的容器几乎总是错的。如果你希望结果对象表现为派生类对象,比如,调用派生类的虚函数等,总是错的。一个使拷贝更高效、正确而且对分割问题免疫的简单的方式是建立指针的容器而不是对象的容器。也就是说,不是建立一个Widget的容器,建立一个Widget*的容器。拷贝指针很快,它总是严密地做你希望的(指针拷贝比特),而且当指针拷贝时没有分割。

Rule4:用empty来代替检查size()是否为0

Rule5:尽量使用区间成员函数代替它们的单元素兄弟
注:区间成员函数就是操作区间的函数,带有 begin(),end()参数的这种形式。
比如:给定两个vector,v1和v2,使v1的内容和v2的后半部分一样的方式?
第一:使用assign方法:
v1.assign(v2.begin()+v2.size()/2,v2.end());
第二:使用循环单个元素push_back操作。

对于所有标准序列容器(vector,list,deque,string)都有效,无论何时你必须完全代替一个容器的内容,你都应该想到赋值。
为什么区间成员函数优于他们的单元素替代品:
区间成员函数是一个像STL算法的成员函数,使用两个迭代器参数来指定元素的一个区间来进行某个操作。不用区间成员函数来解决这个问题,你就必须写一个显式循环。
形式如下:

vector<Widget> v1, v2;      // 假设v1和v2是Widget的vector

v1.clear();
for (vector<Widget>::const_iterator ci = v2.begin() + v2.size() / 2;
        ci != v2.end();
        ++ci)
    v1.push_back(*ci);

我们应该尽量避免手写显式循环。
我们有两个尽量使用区间成员函数代替它们的单元素兄弟的理由:
1. 一般来说使用区间成员函数可以输入更少的代码。
2. 区间成员函数会导致代码更清晰更直接了当。

还有一个就是效率的问题:
重复使用单元素插入而不是一个区间插入就必须处理内存分配,虽然在它里面也有一个令人讨厌的拷贝。就像条款14解释的,当你试图去把一个元素插入内存已经满了的vector时,这个vector会分配具有更多容量的新内存,从旧内存把它的元素拷贝到新内存,销毁旧内存里的元素,回收旧内存。然后它添加插入的元素。条款14也解释了每当用完内存时,大部分vector实现都使它们的容量翻倍,所以插入numValues个新元素会导致最多log2numValues次新内存的分配。条款14也关注了展示该行为的现有实现,所以每次一个地插入1000个元素会导致10次新的分配(包括它们负责的元素拷贝)。与之对比的是(而且,就目前来看,是可预测的),一个区间插入可以在开始插入东西前计算出需要多少新内存(假设给的是前向迭代器),所以它不用多于一次地重新分配vector的内在内存。就像你可以想象到的,这个节省相当可观。

STL容器涉及到区间的函数:
通过区间进行构造
container::container(InputIterator begin, // 区间的起点
InputIterator end); // 区间的终点

区间插入。所有标准序列容器都提供这种形式的insert:
void container::insert(iterator position, // 区间插入的位置
InputIterator begin, // 插入区间的起点
InputIterator end); // 插入区间的终点
关联容器使用它们的比较函数来决定元素插入的位置,所以它们省略了position参数。

区间删除。每个标准容器都提供了一个区间形式的erase,但是序列和关联容器的返回类型不同。序列容器提供了这个:
iterator container::erase(iterator begin, iterator end);
而关联容器提供这个:
void container::erase(iterator begin, iterator end);
两个函数返回不同的原因如果关联容器返回一个迭代器,会导致性能无法接受的下降。

区间赋值:所有标准序列都提供了区间形式的assign。
void container::assign(InputIterator begin, InputIterator end);
关联容器没有这种形式的操作。

Rule6:函数参数的例子
int f(double d);//最基本的函数声明
int f(double (d) );//这个左右括号没有任何左右
Int f(double) //省略了参数名的函数声明

现在我们看看如下的函数声明:
int g(double(*pf)());//g带有一个指向函数的指针作为参数,指向一个没有参数,返回double的函数的指针

下面是完成同一件事的另一种形式,唯一不同的是pf使用非指针语法来声明:
int g(double pf());//同上pf其实是一个指针
同样的,参数名可以省略,所以这是g的第三种声明,去掉了pf这个名字
int g(double());//这里的括号的意思可不是“可有可无”了

list< int > data(istream_iterator(dataFile), istream_iterator< int >());
说明如下:
1. 第一个参数叫做dataFile。它的类型是istream_iterator< int >。dataFile左右的括号是多余的而且被忽略
2. 第二个参数没有名字。它的类型是指向一个没有参数而且返回istream_iterator< int >的函数的指针。

一个更好的解决办法是在数据声明中从时髦地使用匿名istream_iterator对象后退一步,仅仅给那些迭代器名字。以下代码到哪里都能工作:
ifstream dataFile(“ints.dat”);
istream_iterator< int > dataBegin(dataFile);
istream_iterator< int > dataEnd;
list data(dataBegin, dataEnd);

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值