The C++ Standard Libary 学习笔记

Why do I spend my spare time to read this book?

I want to maximize the usage of STL in the real world projects and also konw the architecture of the STL.

 

namespace的使用

对于小程序,可以使用directive(using namespace somespace),这样十分方便,但是对于大程序,通常许多变量,常量,这样使用directive就会出现变量命名冲突,最好使用namespace::variable的形式,这样虽然麻烦,但是绝对可以避免冲突。

 

throw and catch exception

可以throw的异常为 logic_error及其子类,runtime_error及其子类和ios_base::failure,其他异常类型均不能被用户代码throw

所有异常都可以被用户代码捕捉。

 

usage of auto_ptr

1.to avoid the resource leaks when exception handling is used

2.auto_ptr can not share the ownership

3.const keyword before the auto_ptr means you can not change the ownership of the auto_ptr,but you can still change the value of the object pointed by the auto_ptr.The behavior of the const auto_ptr is not like the one of the smart pointer with count-reference technique.

4.If your code is similar with the following:

    auto_ptr<string> ptr1(new string("Bullshit"));

    auto_ptr<string> ptr2(new string("PhD"));    

    ptr1 = ptr2;

    cout<<*ptr2;//runtime error

ptr2 pointing to NULL can not dereference to anything,so it is up to the programer to make sure of it.

5.auto_ptrs are not provided for array as it use delete but not delete[] to delete objects.

6.auto_ptrs don't meet the requirement of the STL containers as when assigning auto_ptrs, the source and sink are different as the source lose it ownership and the sink obtain the ownership.

7.A auto_ptr pointing to NULL can not be assigend as it will delete NULL which causes runtime error.

 

Swap

As most functions in algorithm header files uses the swap function,the general swap

implementation is usefule,but not effective for each conditions,as the following examples.

    class MyContainer {

      private:

        int* elems;        //dynamic array of elements

        int numElems;      //number of elements

 

      public:

        ...

        //implementation of swap()

        void swap(MyContainer& x) {

            std::swap(elems,x.elems);

            std::swap(numElems,x.numElems);

        }

        ...

    };

 

    //overloaded global swap() for this type

    inline void swap (MyContainer& c1, MyContainer& c2)

    {

        c1. swap (c2);  //calls implementation of swap()

    }

You can overload global swap() so that everytime you use a standard function which involves the swap can use the overloaded edition.

So, calling swap() instead of swapping the values directly might result in substantial performance improvements. You should always offer a specialization of swap() for your own types if doing so has performance advantages.

 

Sequence and associated container

The order of the elements in Sequence container is independent with its values, while dependent in associated container.

The containers in the STL are not derived from each other.

 

Only provide good timing operations in STLb

Usually, the STL containers provide only those special member functions that in general have "good" timing ("good" timing normally means constant or logarithmic complexity). This prevents a programmer from calling a function that might cause bad performance.

 

使用如下代码,可以将map中制定的值相同的pair去除

typedef std::map<std::string,float> StringFloatMap;

   StringFloatMap coll;

   StringFloatMap::iterator pos, tmp_pos;

   ...

   //remove all elements having a certain value

   for (pos = c.begin(); pos != c.end(); ) {

       if (pos->second == value) {

           c.erase(pos++);

       }

       else {

           ++pos;

       }

   }

 

 

Associated Containers

They are typically implemented with binary search tree.

 

Special Containers

Stack,queue and priority queue all make use of the framework containers in STL.

 

When use iterators to nagivate a containers,it is better to use preincrement(predecrement) than postincrement(postdecrement),as the post versions involve a temporary assignment.

 

This can adjust any stl containers.

for (pos = coll.begin(); pos != coll.end(); ++pos) {

       ...

}

while this cannot

for (pos = coll.begin(); pos < coll.end(); ++pos) {

       ...

}

 

The range used in STL is half open,so when you search a range ,the end iterator should be increment before being used.

 

当使用多range目的地rangesize必须大于源路径。

 

Assignement and swap()

the assignement takes linear complexity but the swap founction only operator the internal pointers in the contaners which cost constant complexity.

 

Vector Elements Accessing Operation

Only the at() opertaion implements range checking,others not.

The result is undefined with index out of range when applying on these operations.

 

Inserting and Removing

Inserting and removing invalides the references,pointers and iterators that refer to these elements.

 

The Remove founction only logically remove the elements,but the container member function remove the elements physically.

 

You cannot change the value of the elements in the set/multiset as it will ruin the order.You can remove an element and change the value of it and insert it to the set to achive it.

As a result,

1)Sets and multisets don't provide operatrions for direct element access.

2)Indirect access via iterators has the constraint that,from the iterator's oint of view,the element value is constant.

 

Modifying algorithm cannot apply to set container.

 

The usage of Set

set容器将内部的元素按制定的规制排序,这样就可以在log级的复杂度内查找,添加和删除元素。

set中的元素不能修改,因为会改变顺序。只能将要修改的元素先删除,然后在插入。从迭代器的角度来看,他的元素都是不可改变的。

 

In practice,predicating which container type is the best is often diffcult.The big advantage of the STL is that you can try different versions without much effort.The major work-implementing the different data structures and algorithms-is done.You have only to combine them in a way that is best for you.

 

Anything that behaves like an iterator is an iterator

 

Output iterator has no comparation operations as it does not know the end,while forward iterator has as it knows.

 

在使用Random Iterator如果你不是一步一步的迭代容器而是通过+=step来访问如果step>=2,那么就不能用!=collection.end()来判断结束因为可能跨过end()的位置这样就可能出现死循环。

 

迭代器被reverse后,会潜质一格,是为了维持半闭半开。

 

inserter在插入元素后,会一直指向原来的元素,如下面的代码:

    list<int> coll;

    back_insert_iterator< list<int> > iter(coll);

    for(int i = 1; i <= 9; ++i){

         iter = i;

    }

    PRINT_ELEMENTS(coll,"list: ");

  

    insert_iterator< list<int> > itr(coll,++coll.begin());

    itr = 99;

    itr = 100;

    itr = 101;

   

    PRINT_ELEMENTS(coll,"list: ");

输出如下:

list: 1 2 3 4 5 6 7 8 9

list: 1 99 100 101 2 3 4 5 6 7 8 9

 

ostream_iterator<T>(cout," ") 其中cout相当于一容器将所有的字符放入cout那么就显示在显示器上

 

The advantages of functors:

1. It has state which can do something that the pure function can not

2. It a type which can be used in template to specify a certain function.

3. It is faster.

 

Change Functors Passing Style

1.You can requalify the algorithm template to make sure that it require the reference of the functors not the value,such as following code:

   

    vector<int> coll;

    IntSequence seq(1);//functor

    generate_n<back_insert_iterator<vector<int> >,int,IntSequence&>(back_inserter(coll),4 ,seq);

    PRINT_ELEMENTS(coll,"generate n:");

    generate_n/*<back_insert_iterator<vector<int> >,int,IntSequence&>*/(back_inserter(coll),5,seq);

    PRINT_ELEMENTS(coll,"generate n:");

    generate_n(back_inserter(coll),2,seq);

    PRINT_ELEMENTS(coll,"generate n:");

2. apply the for_each alogrithm,the follwing code shows the sample:

 

    list<int> coll;

    generate_n(back_inserter(coll),100,IntSequence(1));

    MeanValue mean = for_each(coll.begin(),coll.end(),MeanValue());

    cout<<"Mean value of sum 1 to 100: "<<mean.mean()<<endl;

 

Functor adpters can both apply to member functions and ordinary functions.

 

函数重载会对函数指针产生混淆

 

for_each 修改值时,需要传引用,transform修改值时,需要传值

 

search for subrange,subrange中出现的值必须连续,而find_first_of就不需要。

 

The removeing algorithms actually do logically remove the elements by overwriting them with elements which are not removed and return the logical new end iterator.

 

Sorting all elements once is always fater than keeping them sorted always!

 

sort alogrithm based on quick-sort algorithm which is nlogn on average and n^2 in the worst case;

patial_sort based on heap-sort algorithm which is slower than quick sort by factor of 2 to 5;

stable_sort based on mergesort which require more memory.

 

Max heap with criterion less<> sorts elemnts in asending,while min heap with criterion greater<> sorts elemnts in desending.

 

算法中还存在一些数学计算方面的STL,估计是为了编写数学相关库的人而设计的。

 

优先队列输出比较规则的逻辑大

 

bitset可以把十进制变成二进制,同时也可以把二进制变成十进制。

 

一般不会用c-string,只有向前兼容是将string转化为c-string,有三个函数可以转化

c_str()(with '/0' appended),data()(without/0 appended)copy()

 

STL提供complex(复数)的操作

IO库中的类可以通过exception方法来制定当异常发生时投出异常。

 

输入、输出控制变量其实就是函数指针,重载了输出<<操作符,如下:

    ostream& ostream::operator << (ostream& (*op)(ostream&)){

        return (*op)(*this);

 

You can define you own manipulators,the following is a examle:

   #include <istream>

   #include <limits>

 

   template <class charT, class traits>

   inline

   std::basic_istream<charT,traits>&

   ignoreLine (std::basic_istream<charT,traits>& strm)

   {

       // skip until end-of-line

       strm.ignore(std::numeric_limits<int>::max(),strm.widen('/n'));

 

       // return stream for concatenation

       return strm;

   }

    }

 

 

bauto-ptr is not provided for arrays.

在其析构函数中,调用的是delete而非delete[],因此不能对数组使用。

 

auto_ptrs don't meet the requirements for container elements.

因为将其拷贝至容器中,会导致内部指针所有权的转移,源内容与目标内容不一致。

 

用前置自增自减操作符。

后置需要返回原来的对象,可能需要用临时对象保存,影响效率。尽量使用前置操作符。

 

All containers provide value rather than reference semantics.

容器元素必须能够被拷贝(提供拷贝构造函数);大型对象采用传值开销较大,应利用指针。

 

List is transaction safe.

 

For all node-based containers (lists, sets, multisets, maps and multimaps), any failure to construct a node simply leaves the container as it was.

 

All array-based containers (vectors and deques) do not fully recover when an element gets inserted.

由于其能够在任意地方插值,为了恢复,必须将元素另外保存供恢复时使用,开销过大。而删除操作只存在于队首或队尾,无需另行保存元素,因此删除操作失败可以完全恢复。

 

Destructor never throws.

否则资源无法被释放。

 

The STL doesn’t guarantee the safety.

The STL just guarantees the performance.

 

Prefer “empty()” to “size() == 0”.

empty 实现效率可能更高。

 

swap() 时间效率为常量

swap() 交换内部指针。

 

Prefer “container::reserve(int capacity)” to “container::container(int capacity)”.

为容器预留空间时,构造空容器,使用reserve();若调用构造函数 container(int capacity) 预留空间,会调用默认构造函数,影响效率。

 

Iterators remain valid until an element with a smaller index gets inserted or removed, or reallocation occurs and capacity changes in a vector.

 

The deque is implemented typically as a bunch of individual blocks.

The internal structure has one more indirection to access the elements, so element access and iterator movement of deques are usually a bit slower.  Iterators must be smart pointers of a special type rather than ordinary pointers because they must jump between different blocks.

 

Deques provide no support to control the capacity and the moment of reallocation.

In particular, any insertion or deletion of elements other than at the beginning or end invalidates all pointers, references, and iterators that refer to elements of the deque. However, reallocation may perform better than for vectors, because according to their typical internal structure, deques don't have to copy all elements on reallocation.

 

Blocks of memory might get freed when they are no longer used, so the memory size of a deque might shrink.

 

In set or multi-set, you may not change the value of an element directly because this might compromise the correct order which is achieved by automatic sorting.

You must remove the element that has the old value and insert a new element that has the new value. All elements are considered const.

 

In set or multi-set, you can define the sorting criterion in two ways: as a template parameter or a constructor parameter.

set 或者 multi-set 的类型包括排序规则,是由模版参数指定的,默认为less。只有类型相同的setmulti-set)在能够拷贝、赋值、比较。如果排序规则模板参数类型可以构造不同的排序规则,可将具体的排序规则实例作为参数传递给构造函数,使得同种类型的setmulti-set)可以拥有不同的排序规则。

 

Maps and multi-maps sort their elements automatically according to the element's keys.

因此通过key查找效率很高,而通过value查找效率较差。从其迭代器观点,所有key都是const的,如果需要修改key,必须先删除再重新插入,否则可能会破坏顺序。

 

When removing elements in maps and multi-maps, there’s a danger that will you remove an element to which your iterator is referring.

以下为错误例子:

typedef std::map<std::string,float> StringFloatMap;

StringFloatMap coll;

StringFloatMap::iterator pos;

...// other operations

for (pos = coll.begin(); pos != coll.end(); ++pos) {

if (pos->second == value) {

coll. erase (pos); // RUNTIME ERROR !!!

}

}

当执行++pos时,pos可能已经被删除,变为无效,造成错误。

正确过程如下:

typedef std::map<std::string,float> StringFloatMap;

StringFloatMap coll;

StringFloatMap::iterator pos, tmp_pos;

...

//remove all elements having a certain value

for (pos = c.begin(); pos != c.end(); ) {

if (pos->second == value) {

c.erase(pos++);

}

else {

++pos;

}

}

c.earse(pos++)中,后置自增运算符重载实现时,先保存了pos的拷贝,然后增加pos的值,最后返回所保存的原来的pos的拷贝。在调用earse时,实际的pos已经指向了下一个位置,而删除的是返回的拷贝,pos依然有效。

 

The automatic sorting of associative containers does not mean that these containers perform better when sorting is needed.

This is because an associative container sorts each time a new element gets inserted. An often faster way is to use a sequence container and to sort all elements after they are all inserted by using one of the several sort algorithms.

 

For all fundamental data types, such as pointers, you are not allowed to modify temporary values. For structures and classes, however, it is allowed.

在某些实现中 vector 的迭代器是原始指针,因此

std::vector<int> v;

//… other operation;

If(v.size() > 1)

v.sort(++v.begin(), v.end());

可能会失败(取决于vector的实现);为了保证移植性,应该使用

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

v.sort(++I, v.end());

string也有同样的问题。

 

You should not define an istream iterator before you really need it.

在构造 istream iterator 时已经读取了第一个元素。否则,将无法读取第一个元素。

 

Two istream iterators are equal if both are end-of-stream iterators and thus can no longer read, or both can read and use the same stream.

 

Any forward iterator is a kind of input iterator. Any forward iterator is not a kind of output iterator.

struct forward_iterator_tag : public input_iterator_tag {…};

 

Function objects are passed by value rather than by reference.

所以,算法使用函数对象时不会改变原始函数对象的状态。其优点是可以直接传递常量和临时表达式(右值);缺点是无法通过算法改变原始函数对象状态。

为了传递引用,可以显示地指定算法地模板参数为函数对象引用类型;或者使用  for_each() 算法的返回值。

 

函数对象比函数指针效率可能更高。

函数对象在编译优化时可以内联展开,而函数指针必须压栈,因此函数对象效率可能更高。

 

A predicate should not change its state due to a call, and a copy of a predicate should have the same state as the original.

某些算法实现会调用同一个谓词多次。如果谓词内部状态改变,则不能保证对于相等的值谓词产生相同地结果。为了保证谓词不改变其内部状态,将其 operator( ) 声明为 const 函数。

 

By using bind2nd it is also possible to pass one argument to the called member function.

例如:

for_each (coll.begin(), coll.end(),

bind2nd(mem_fun_ref (&Person::printWithPrefix),

"person: "));

之所以要用 bind2nd,是因为类成员函数的第一个参数为隐式的 this 指针,

 

mem_fun adapters are for sequences that contain pointers to elements.

Probably mem_fun_ptr would have been a less confusing name for them.

 

The member functions called by mem_fun_ref and mem_fun must be constant member functions. Both mem_fun_ref and mem_fun can call member functions with zero or one argument.

 

Chapter  9

You should avoid using for_each() when possible.

其他算法有明确的目的,因此应该尽量使用其他算法。

 

For_each( ) 算法中,其操作的参数必须为按引用传递。

因为在 for_each( ) 算法中,其接受的操作在其内部是修改操作的参数。如果操作的参数按照传值传递,则无法则无法改变实际的值。

transform 算法返回一个值到目标范围,因此传递给 transform 算法的操作的参数可以按值传递。

 

You can't use associative containers as a destination for modifying, mutating, sorting & removing algorithms.

联合容器的元素都是常量的,以保证不会破坏联合容器的顺序性。

 

Removing algorithms remove elements logically only by overwriting them with the following elements that were not removed.

Thus, they do not change the number of elements in the ranges on which they operate. Instead, they return the position of the new "end" of the range.

 

选择合适的排序算法。

Sort( )——快排序,平均时间复杂度n*logn,最坏时间复杂度n2

Partial_sort( )——堆排序,任何情况下时间复杂度为n*logn

Stable_sort( )——堆排序,对于相等元素保留其原来的相对位置,有足够内存复杂度为n*logn,否则为n*log2n

 

If the range is sorted, you should use the lower_bound(), upper_bound(), equal_range(), or binary_search() rather than find() and find_if().

一切为了效率。联合容器使用相应的类成员函数。

 

Removing Algorithms can not change the number of elements.

They only move logically by overwriting "removed" elements with the following elements that were not removed. They return the new logical end of the range.

 

Unique removes all elements that are equal to the previous elements.

因此,只有当容器拍过序,或者所有重复的元素都是相邻的,才能保证使用unique后不再有相同的元素。

 

reverse_copy() may be slightly more efficient than using copy() with reverse iterators.

 

random_shuffle() uses its optional operation as a non-constant reference.

random number generators typically have a local state. Old global C functions such as rand() store their local state in a static variable. However, this has some disadvantages: For example, the random number generator is inherently thread unsafe, and you can't have two independent streams of random numbers. Therefore, function objects provide a better solution by encapsulating their local state as one or more member variables. Thus, the random number generator can't be constant because it has to change its local state while generating a new random number. However, to have the random number generator non-constant, you could still pass it by value instead of passing it by non-constant reference. In this case each call would copy the random number generator and its state so that you get the same random sequence each time you pass the generator to the algorithm. Thus the generator is passed as non-constant reference.

If you need the same random number sequence twice, you can simply copy it.

 

Unlike sort(), partial_sort() does not sort all elements, but stops the sorting once the first elements up to end of the sorted range are sorted correctly.

因此,如果只需要容器中某一部分排序,使用 partial_sort 节约时间。

Sort 有更好的平均时间复杂度,而 partial_sort 的最坏时间复杂度更好。

 

set_union()算法中,结果可能包含重复的元素。

The number of occurrences of equal elements in the destination range is the maximum of the number of their occurrences in both source ranges.

 

set_intersection()算法中,结果可能包含重复的元素。

The number of occurrences of equal elements in the destination range is the minimum number of their occurrences in both source ranges.

 

set_difference()算法中,结果可能包含重复的元素。

The number of occurrences of equal elements in the destination range is the difference between the number of their occurrences in the first source range less the number of occurrences in the second source range. If there are more occurrences in the second source range, the number of occurrences in the destination range is zero.

 

set_symmetric_difference()算法中,结果可能包含重复的元素。

The number of occurrences of equal elements in the destination range is the difference between the numbers of their occurrences in the source ranges.

 

chapter 10

Stack 使用 deque 作为其缺省的容器。

Unlike vectors, deques free their memory when elements are removed and don't have to copy all elements on reallocation.

 

You can't change the number of bits in a bitset.

The number of bits is the template parameter. If you need a container for a variable number of bits or Boolean values, you can use the class vector<bool>

 

Sorting all elements once is usually faster than keeping them sorted always.

 

Unlike C-strings, objects of class string have no special character '/0' at the end of the string.

 

Always use string::size_type and not int or unsigned for the return type when you want to check the return value of a find function.

Otherwise, the comparison with string::npos might not work.

 

Accessing a single character of the string is done with operator [ ].

This operator does not check whether the index of the string is valid. A safer way to access a character is to use the at() member function. However, such a check costs runtime.

 

string::size_type 是无符号整型。

size_type类型与int类型的变量比较大小时,可能需要将size_type类型的变量转换为int类型,否则int类型的变量会被隐式转换为无符号整型,造成比较错误。

 

data( ) and c_str( ) member functions of string return an array that is owned by the string.

Thus, the caller must not modify or free the memory.

 

The return value of c_str() and data() is valid only until the next call of a non-constant member function for the same string.

std::string s;

const char* p;

p = s.c_str() ;        //p refers to the contents of s as a C-string

foo (p);   //OK,(p is still valid)

s += "ext" ;    //invalidates p

foo (p); //ERROR: argument p is not valid

 

The concept of capacity for strings is, in principle, the same as for vector containers, however, there is one big difference.

Unlike vectors, you can call reserve() for strings to shrink the capacity. Calling reserve() with an argument that is less than the current capacity is, in effect, a nonbinding shrink request. If the argument is less than the current number of characters, it is a nonbinding shrink-to-fit request. Thus, although you might want to shrink the capacity, it is not guaranteed to happen. The default value of reserve() for string is 0. So, a call of reserve() without any argument is always a nonbinding shrink-to-fit request.

 

Be very careful when using the string value npos and its type.

When you want to check the return value always use string::size_type and not int or unsigned for the type of the return value; otherwise, the comparison of the return value with string::npos might not work. This behavior is the result of the design decision that npos is defined as -1.

To write portable code, you should always use string::size_type for any index of your string type.

 

String iterators are random access iterators.

因此,可以对string使用所有的算法。

 

The caller of equal() has to ensure that the second range has at least as many elements/characters as the first range. Thus, comparing the string size is necessary;

if (s1.size() == s2.size() &&

equal (s1.begin(),s1.end(),

s2.begin(),

nocase_compare))

 

Considering a string as a special kind of vector is dangerous.

There are many fundamental differences between the two. Chief of these are their two primary goals:

The primary goal of vectors is to handle and to manipulate the elements of the container, not the container as a whole. Thus, vectors implementations are optimized to operate on elements inside the container.

The primary goal of strings is to handle and to manipulate the container (the string) as a whole. Thus, strings are optimized to reduce the costs of assigning and passing the whole container.

These different goals typically result in completely different implementations.

 

String::c_str( )返回时加上’/0’,而data( ), copy( )则在最后不加上’/0’

使用这些函数时,要确保得到的是合法的C-Str

 

不能使用complex类对象作为联合容器的元素,除非提供自己的排序规则。

Complex类对象只定义了=!=比较操作符。

 

The assignment operators are the only functions that allow you to modify the value of an existing complex.

 

That the type of the single value has to match exactly the element type of the valarray.

The following statement would fail:

std::valarray<double> va(20);

...

va = 4 * va;    // ERROR: type mismatch

 

You can create subsets easily, but you can't combine them easily with other subsets.

You almost always need an explicit type conversion to valarray. This is because the C++ standard library does not specify that valarray subsets provide the same operations as valarrays.

 

If the subset qualified by a slice is a subset of a constant valarray, the subset is a new valarray. If the valarray is nonconstant, the subset has reference semantics to the original valarray.

 

The difference between failbit and badbit is basically that badbit indicates a more fatal error.

failbit is set if an operation was not processed correctly but the stream is generally OK. Normally this flag is set as a result of a format error during reading.

badbit is set if the stream is somehow corrupted or if data is lost.

 

eofbit normally happens with failbit because the end-of-file condition is checked and detected when an attempt is made to read beyond end-of-file.

After reading the last character, the flag eofbit is not yet set. The next attempt to read a character sets eofbit and failbit, because the read fails.

 

Constants of type iostate are not defined globally. Instead, they are defined within the class ios_base.

Thus, you must always use them with the scope operator or with some object. For example: std::ios_base::eofbit.

std::ios::eofbit is OK because ios is derived from ios_base.

 

You always have to clear error bits explicitly.

If failbit is set, each following stream operation is a no-op until failbit is cleared explicitly.

 

The set bits reflect only what happened sometime in the past.

If a bit is set after some operation this does not necessarily mean that this operation caused the flag to be set. Instead, the flag might have been set before the operation. Thus, goodbit should be set (if it is not known to be set) before an operation is executed if the flags arc then used to tell you what went wrong. Also, after clearing the flags the operations may yield different results.

 

Manipulators are nothing more than functions that are passed to the I/O operators as arguments.

The functions are then called by the operator.

 

There is actually an advantage in using the function notation: It is not necessary to provide the namespace: endl(std::cout).

This is because functions are looked up in the namespaces where their arguments are defined if they are not found otherwise.

 

The field width is never used to truncate output.

Thus, you can't specify a maximum field width.

 

After any formatted I/O operation is performed, the default field width is restored. The values of the fill character and the adjustment remain unchanged until they are modified explicitly.

 

Because of the fact that ordinary C-strings can't grow while values are read, width() or setw() should always be used when reading them with operator >>.

 

It is important to note for streams that are both read and written that it is not possible to switch arbitrarily between reading and writing.

 

At the end of both functions the file opened locally is closed automatically when the corresponding stream goes out of scope. The destructors of the classes ifstream and

ofstream take care of closing the file if it is still open at destruction time.

If a file should be used longer than the scope in which it was created, you can allocate the file object on the heap and delete it later when it is no longer needed.

 

Instead of copying the file contents character-by-character, you could also output the whole contents in one statement by passing a pointer to the stream buffer of the file as an argument to operator <<:

// copy file contents to cout

std::cout << file.rdbuf();

 

Binary should always be used if the contents of a file do not consist of a character sequence but are processed as binary data.

 

Whether a file is opened for reading and/or for writing is independent of the corresponding stream object's class. The class only determines the default open mode if no second argument is used.

This means that files used only by the class ifstream or the class ofstream can be opened for reading and writing. The open mode is passed to the corresponding stream buffer class, which opens the file. However, the operations possible for the object are determined by the stream's class.

 

Note that after the processing of a file, clear() must be called to clear the state flags that are set at end-of-file.

This is required because the stream object is used for multiple files. The member function open() does not clear the state flags. open() never clears any state flags. Thus, if a stream was not in a good state, after closing and reopening it you still have to call clear() to get to a good state. This is also the case, if you open a different file.

 

pos_type is not an integral value or simply the position of the character as an index.

This is because the logical position and the real position can differ. For example, in MS-DOS text files, newline characters are represented by two characters in the file even though it is logically only one character.

The exact definition of pos_type is a bit complicated: The C++ standard library defines a global template class fpos<> for file positions. Class fpos<> is used to define types streampos for char and wstreampos for wchar_t streams. These types are used to define the pos_type of the corresponding character traits. And the pos_type member of the traits is used to define pos_type of the corresponding stream classes.

 

You can only have one output stream that is tied to this stream. However, you can tie an output stream to different streams.

 

By default, the standard input is connected to the standard output using this mechanism:

// predefined connections:

std::cin.tie (&std::cout);

std::wcin.tie (&std::wcout);

所以能够看见自己的输入。

 

The fact that the stream buffer is not destroyed applies only to basic_istream and basic_ostream. The other stream classes destroy the stream buffers they allocated originally, but they do not destroy stream buffers set with rdbuf().

 

A stream can be redirected by setting a stream buffer.

 

The old buffer should always be saved and restored later.

 

Although two different stream objects are used for reading and writing, the read and write positions are tightly coupled when they use the same IO channel.

seekg() and seekp() call the same member function of the stream buffer.

 

With the flag ios::app or ios::ate, the characters written to a string stream can be appended to an existing string.

std::string s;

...

std::ostringstream os (s, ios::out|ios::app);

os << 77 << std::hex << 77;

However, this means that the string returned from str() is a copy of the string s, with a decimal and a hexadecimal version of 77 appended. The string s itself is not modified.

 

If an I/O operator uses a function for unformatted I/O or operates directly on the stream buffer, the first thing to be done should be the construction of a corresponding sentry object.

 

The characters in the range from pbase() to pptr() (not including the character pointed to by pptr()) are already written but not yet transported (flushed) to the corresponding output channel.

 

You have to disable the synchronization before any other I/O operation.

Calling this function Sync_with_stdio after any I/O has occurred results in implementation-defined behavior.

 

In C++, wchar_t is a keyword rather than a type definition.

Thus, it is possible to overload all functions with this type.

 

C established the convention to return a character as int instead of as char from functions reading characters. This technique was extended in C++.

The character traits define char_type as the type to represent all characters, and int_type as the type to represent all characters plus EOF.

 

When using locales in C++, it is important to remember that the C++ locale mechanism is only loosely coupled to the C locale mechanism.

There is only one relation to the C locale mechanism: The global C locale is modified if a named C++ locale object is set as the global locale. In general, you should not assume that the C and the C++ functions operate on the same locales.

 

A locale object is used as a container of different facets.

The actual dependencies on national conventions are separated into several aspects that are handled by corresponding objects. An object dealing with a specific aspect of internationalization is called a facet.

To access an aspect of a locale, the type of the corresponding facet is used as the index. The type of the facet is passed explicitly as a template argument to the template function use_facet(), accessing the desired facet.

 

A facet in a locale is accessed using the type of the facet as the index.

Because each facet exposes a different interface and suits a different purpose, it is desirable to have the access function to locales return a type corresponding to the index. This is exactly what can be done with a type as the index. Using the facet's type as an index has the additional advantage of having a type-safe interface.

 

Locales are immutable.

This means the facets stored in a locale cannot be changed (except when locales are being assigned). Variations of locales are created by combining existing locales and facets to create a new locale.

 

Copying a locale is considered to be a cheap operation.

Basically, it consists of setting a pointer and increasing a reference count. Creating a modified locale is more expensive. In this case, a reference count for each facet stored in the locale has to be adjusted. Although the standard makes no guarantees about such efficient behavior, it is likely that all implementations will be rather efficient for copying locales.

 

The name of a locale is maintained if the locale was constructed from a name, or one or more named locales.

However, again, the standard makes no guarantees about the construction of a name resulting from combining two locales.

 

Every class F that conforms to the following two requirements can be used as a facet:

1.      F derives publicly from class locale::facet. This base class mainly defines some mechanism for reference counting that is used internally by the locale objects. It also declares the copy constructor and the assignment operator to be private, thereby making it infeasible to copy or to assign facets.

2.      F has a publicly accessible static member named id of type locale::id. This member is used to look up a facet in a locale using the facet's type. The whole issue of using a type as the index is to have a type-safe interface. Internally, a normal container with an integer as the index is used to maintain the facets.

 

 

The standard facets conform not only to these requirements but also to some special implementation guidelines. Although conforming to these guidelines is not required, doing so is useful. The guidelines are as follows:

1.         All member functions are declared to be const. This is useful because use_facet() returns a reference to a const facet. Member functions that are not declared to be const can't be invoked.

2.      All public functions are nonvirtual and delegate each request to a protected virtual function. The protected function is named like the public one, with the addition of a leading do_. For example, numpunct::truename() calls numpunct::do_truename(). This style is used to avoid hiding member functions when overriding only one of several virtual member functions that has the same name. For example, the class num_put has several functions named put(). In addition, it gives the programmer of the base class the possibility of adding some extra code in the nonvirtual functions, which is executed even if the virtual function is overridden.

 

Beware that you don't mix elements with different allocators.

 

 


 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值