Thinking in C++第二卷笔记之STL容器部分

Thinking in C++第二卷笔记之STL容器部分

1、容器

All the containers in the standard library hold copies of the objects you place in them, and 

expand their resources as needed, so your objects must be copy-constructible (have an 

accessible copy constructor) and assignable (have an accessible assignment operator). The

 key difference between one container and another is the way the objects are stored in 

memory and what operations are available to the user.

2、多重成员资格(multiple membership)

由于存在多重成员问题,使得自动删除指针成为问题。一个容器持有指向某个对象的指针,可能该指针在另一个容器中也出现。如果在一个容器中删除通过该指针删除了这个对象,则另外一个容器将访问不到这个对象了。

这个问题我们可以在链表中存储对象而不是指针来解决。

3、迭代器大致分5类:输入,输出,前向,双向,随机迭代器。

STL的总结

http://blog.163.com/zhoumhan_0351/blog/static/3995422720103174417603/

标准模板类(STL)(四),容器的比较、对比和总结

http://blog.163.com/zhoumhan_0351/blog/static/3995422720102267442299/

标准模板类(STL)(五),算法和函数

http://blog.163.com/zhoumhan_0351/blog/static/39954227201022783524890/

    随即迭代器类似于一个指针。它具有双向迭代器的所有功能,再加上一个指针所有的功能,除了没有一种null空迭代器和空指针相对应。可以说,一个随即迭代器可以一个指针那样进行任何操作,包括operator[]进行索引。

struct input_iterator_tag {};

struct output_iterator_tag {};

struct forward_iterator_tag :

  public input_iterator_tag {};

struct bidirectional_iterator_tag :

  public forward_iterator_tag {};

struct random_access_iterator_tag :

  public bidirectional_iterator_tag {};

4、向一个序列的中间添加元素的back_inserter(),它通过自动调用插入函数insert()而不是“压入”函数。

#include "vector"

#include "iterator"

#include "iostream"

using namespace std;

int a[5];

int main()

{

 vector<int> ci;

 copy(a, a + sizeof(a)/sizeof(vector<int>::value_type),

    back_inserter(ci));

    copy(ci.begin(), ci.end(),ostream_iterator<

 typename vector<int>::value_type>(cout, " "));

}

template<class Cont>

    back_insert_iterator<Cont> back_inserter(Cont& x);

template<class Cont, class Iter>

    insert_iterator<Cont> inserter(Cont& x, Iter it);

5、Although it is possible to create an istream_iterator<char> and 

ostream_iterator<char>, these actually parse the input and thus will, for 

example, automatically eat whitespace (spaces, tabs, and newlines), which is 

not desirable if you want to manipulate an exact representation of an istream. 

Instead, you can use the special iterators istreambuf_iterator and 

ostreambuf_iterator, which are designed strictly to move characters.Although 

these are templates, they are meant to be used with template arguments of 

either char or wchar_t。

istreambuf_iterator(streambuf_type *sb = 0) throw();

istreambuf_iterator(istream_type& is) throw();

The first constructor initializes the input stream-buffer pointer with sb. The 

second constructor initializes the input stream-buffer pointer with 

is.rdbuf(),then (eventually) attempts to extract and store an object of type E.

输入/输出流迭代器会自动吃掉空白字符,如果希望用一个输入流的精确地表现这样的动作,是不可取的,应当用输入/输出流缓冲迭代器。

#include <algorithm>

#include <fstream>

#include <iostream>

#include <iterator>

using namespace std;

int main() {

 ifstream in("C:\\copy.txt");

  // Exact representation of stream:

  istreambuf_iterator<char> isb(in), end;

  ostreambuf_iterator<char> osb(cout);

  while(isb != end)

    *osb++ = *isb++; // Copy 'in' to cout

  cout << endl;

  ifstream in2("C:\\copy.txt");

  // Strips white space:

  istream_iterator<char> is(in2), end2;

  ostream_iterator<char> os(cout);

  while(is != end2)

    *os++ = *is++;

  cout << endl;

} ///:~

6、操作未初始化的缓冲区

用输出迭代器raw_storage_iterator。它提供了使算法能够将其结果存储到未初始化的内存的能力。其构造函数持有一个指向某原始内存储区的迭代器,并且运算符operator=将一个对象分配给那个原始内存。

template<class FwdIt, class T>

    class raw_storage_iterator

         : public iterator<output_iterator_tag, void, void> {}

#include <iostream>

#include <iterator>

#include <algorithm>

using namespace std;

class Noisy{

public:

Noisy() {}

Noisy(int i_):i(i_){}

~Noisy(){cout<<"Destory"<<endl;}

operator int() const 

{ return i; }  //转换为int打印

private:

int i;

};

int main() {

  const int QUANTITY = 10;

  // Create raw storage and cast to desired type:

  Noisy* np = reinterpret_cast<Noisy*>(

    new char[QUANTITY * sizeof(Noisy)]);

  raw_storage_iterator<Noisy*, Noisy> rsi(np);

  for(int i = 0; i < QUANTITY; i++)

    *rsi++ = Noisy(); // Place objects in storage

  cout << endl;

  copy(np, np + QUANTITY,

    ostream_iterator<Noisy>(cout, " "));

  cout << endl;

  // Explicit destructor call for cleanup:

  for(int j = 0; j < QUANTITY; j++)

    (&np[j])->~Noisy();

  // Release raw storage:

  delete reinterpret_cast<char*>(np);

} ///:~

7、成员函数swap在两个容器间交换所有东西,这些容器通常存储指针。而非成员函数swap()算法通常采用赋值的方式来交换其参数。对于标准容器来说,已通过模板的特化定制为调用成员函数swap()了。

8、vector的内存分配溢出问题

1)Allocating a new, bigger piece of storage.

2)Copying all the objects from the old storage to the new (using the 

copy-constructor).

3)Destroying all the old objects (the destructor is called for each one).

4)Releasing the old memory.

也就是如果存储的东西大于vector的默认空间容量,则重分配,并将先前的东西重新拷贝到新的地方。这通常是不能容忍的。

解决方法是:

1)事先知道要存储多少个数据,用reserve()来告诉vector预分配多大的存储区。Note that the use of reserve( ) is different from using the vector

 constructor with an integral first argument; the latter initializes a prescribed 

number of elements using the element type’s default constructor.

   2)用其它的容器。if you’re creating all your objects in one part of the 

program and randomly accessing them in another, you may find yourself 

filling a deque and then creating a vector from the deque and using the vector 

for rapid indexing. You don’t want to program this way habitually—just be 

aware of these issues (that is, avoid premature optimization).

使用vector最安全的方法就是一次性的填入所有的元素。使用其的最佳理由是“它看起来很像一个数组”。

在vector重分配内存的过程中,把其存储的内容移到别处,则原迭代器的指针就指下一个未知的地方。

#include <iterator>

#include <iostream>

#include <vector>

using namespace std;

int main() {

  vector<int> vi(10, 0);

  ostream_iterator<int> out(cout, " ");

  vector<int>::iterator i = vi.begin();

  *i = 47;

  copy(vi.begin(), vi.end(), out);

  cout << endl;

  // Force it to move memory (could also just add

  // enough objects):

  vi.resize(vi.capacity() + 1);

  // Now i points to wrong memory:

  *i = 48;  // Access violation

  copy(vi.begin(), vi.end(), out); // No change to vi[0]

} ///:~

9、双端队列

    deque的典型实现是利用多个连续的存储块。它不需要重新分配存储区,所以deque比vector更有效率。只有在确切知道到底有多少个对象的时候,vector才是最佳的选择。

#include <cstddef>

#include <ctime>

#include <deque>

#include <fstream>

#include <iostream>

#include <iterator>

#include <sstream>

#include <string>

#include <vector>

using namespace std;

int main() {

  char* fname = "C:\\Rand.doc";

  ifstream in("fname");

  vector<string> vstrings;

  deque<string> dstrings;

  string line;

  // Time reading into vector:

  clock_t ticks = clock();

  while(getline(in, line))

    vstrings.push_back(line);

  ticks = clock() - ticks;

  cout << "Read into vector: " << ticks << endl;

  // Repeat for deque:

  ifstream in2(fname);

  ticks = clock();

  while(getline(in2, line))

    dstrings.push_back(line);

  ticks = clock() - ticks;

  cout << "Read into deque: " << ticks << endl;

  // Now compare indexing:

  ticks = clock();

  for(size_t i = 0; i < vstrings.size(); i++) {

    ostringstream ss;

    ss << i;

    vstrings[i] = ss.str() + ": " + vstrings[i];

  }

  ticks = clock() - ticks;

  cout << "Indexing vector: " << ticks << endl;

  ticks = clock();

  for(size_t j = 0; j < dstrings.size(); j++) {

    ostringstream ss;

    ss << j;

    dstrings[j] = ss.str() + ": " + dstrings[j];

  }

  ticks = clock() - ticks;

  cout << "Indexing deque: " << ticks << endl;

  // Compare iteration

  ofstream tmp1("fname"), tmp2("fname");

  ticks = clock();

  copy(vstrings.begin(), vstrings.end(),

    ostream_iterator<string>(tmp1, "\n"));

  ticks = clock() - ticks;

  cout << "Iterating vector: " << ticks << endl;

  ticks = clock();

  copy(dstrings.begin(), dstrings.end(),

    ostream_iterator<string>(tmp2, "\n"));

  ticks = clock() - ticks;

  cout << "Iterating deque: " << ticks << endl;

} ///:~

说明:clock()这个函数返回从“开启这个程序进程”到“程序中调用clock()函数”时之间的CPU时钟计时单元(clock tick)数,在MSDN中称之为挂钟时间(wal-clock);若挂钟时间不可取,则返回-1。不是秒数。

10、序列容器的转换

you may need the efficiency of a deque when adding objects to the container 

but the efficiency of a vector when indexing them. Each of the basic sequence 

containers (vector, deque, and list) has a two-iterator constructor (indicating 

the beginning and ending of the sequence to read from when creating a new 

object) and an assign( ) member function to read into an existing container, so 

you can easily move objects from one sequence container to another.

#include <algorithm>

#include <cstdlib>

#include <deque>

#include <iostream>

#include <iterator>

#include <vector>

using namespace std;

class sequence{

public:

int a;

sequence(){a=0;}

inline int operator()(){

return rand()%100;

}

};

int main() {

  int size = 25;

  deque<int> d;

  generate_n(back_inserter(d), size,sequence());

  cout << "\n Converting to a vector(1)" << endl;

  vector<int> v1(d.begin(), d.end());

  cout << "\n Converting to a vector(2)" << endl;

  vector<int> v2;

  v2.reserve(d.size());

  v2.assign(d.begin(), d.end());

  cout << "\n Cleanup" << endl;

} ///:~

    从序列的两端插入或删除元素,合理地快速遍历,以及使用operator[]进行相当快速的随机访问。

11、operator[]和at()都可以访问元素,但是[]不进行越界检查,而at()要进行越界检查。如查越界,则抛出一个异常。

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值