c++ Primer(4th)学习笔记

c++ Primer(4th)学习笔记,按章节,抓重点.

第一部分:c++基础
一些重点;
形参的初始化与变量的初始化一样:如果形参具有非引用类型,则复制实参的值;如果形参位引用类型,则它只是实参的别名.
默认实参(string screenInit(string::size_type height=24,string::size_type width=80,char background = ''))
普通形参(int): 在函数体里建立局部副本.
指针形参(int*):指针值不变,所指对象在函数体里改变.如需保护(只读),则const int*
引用形参(int&):将实参传入函数体,可以修改其值,还可以返回额外的值.const int&可以避免复制副本,直接只读.
数组形参(int array[]):数组对象自动转换为指向数组首地址的指针
函数形参():函数形参也自动转换为指向函数的指针
容器形参(vector<int>::const_iterator beg)
命令行选项:   应用:main -d -o ofile data0
   定义:int main(int argc,char *argv[]) {...}
const fun(): 是返回一个const类型的值,它要求接受类型也是const的,否则会编译错误.  
fun() const: 多是用于类里面的函数,是保证在该函数中各个数据只是传值,并不发生值的变化,比如说a++就不可以.

static fun():使得函数只能在本文件或类中可见,在其他地方是不可见的.
static_cast:   一般的类型转换,no run-time check.通常,如果你不知道该用哪个,就用这个.
dynamic_cast:  通常在基类和派生类之间转换时使用,run-time cast  
const_cast:    主要针对const和volatile的转换.  
reinterpret_cast:   用于进行没有任何关联之间的转换,比如一个字符指针转换为一个整形数.


标准库类型
  1,namespace, using声明
 using std::cin; #include <iostream> 声明std标准库中的cin,  ::作用域操作符
 
  2,string类型
 using std::string;   #include <string> string标准库负责管理字符相关的内存和提供各种可变长度字符串的操作.
    string标准库自带的几个构造函数:
 string s1;默认 string s2(s1); string s3("value");  string s4(n, 'c'); 
    string的读写:
 1,读 cin >> s1 >> s2;   输入第1个到s1,第2个到s2,去掉空格的.
 2,写 cout << s1 << s2 << end1;  helloworld
 3,读入无限 while( cin >> word )  cout << word << end1;
 4,读入一行 while( getline( cin , word )) cout << word << end1;
    string的操作:
 s.empty()判断 s.size()大小 s[n]下标  s1+s2相加 s1 = s2赋值 s1 == s2(!= <= >=,大小按字典排列)比较
 对于string的size,它的类型必须是string::size_type(这种类型是配套类型,也叫库类型使与机器无关,unsigned型)
 string s3 = s1 + "," + s2 + "/n";
 s[n] = '*'; 下标操作可以是左值
 string中的字符处理函数:#include <cctype.h>
 isalnum(c)字母或数字   isalpha(c)字母 ......

  3,vector类型
    #include <vactor>
    using std::vector; vector容器是同一类元素的集合,也是一个类模板(class template),包含系列类定义和函数定义,
    而使用不同的元素(string,int或sales_itme类类型对象).
    定义和初始化:
    vactor<int> ivec1;
    vactor<int> ivec2(ivec1);
    vector<string> svec(10,"hi!");  一般先定义再添加,容器的优点就是动态增长.
    vector<int> fvec(10);   10个元素,标准库调用默认构造函数默认值为0
    vector<string> svec(10);        10个元素,默认为空string
    操作:
    v.empty(); v.size();安全的泛型编程概念 v.push_back(t);插入 
    v[n];下标只能读,不能插入元素,string可以 v1=v2,空的不能读,缓冲区溢出 v1==v2;(!=;<=或>=)

  4,迭代器
    除了用下标访问容器,标准库提供了迭代器(iterator)检查容器元素并遍历元素.只有少数容器支持下标.
    每种容器定义了自己的iterator类型,都支持iterator操作.
    vector<int>::iterator iter;
    每种容器都定义了一对begin,end函数.用于返回迭代器. vector<int>::iterator iter = ivec.begin();
    迭代器的解引用操作符*和自增运算符++, *iter=0; ++iter;
    ==;!=操作
    for ( vector<int>::iterator iter = ivec.begin(); iter = ivec.end(); ++iter )
 *iter = 0;
    const_iterator用来做只读迭代器, for ( vector<int>::const_iterator iter = ivec.begin(); iter = ivec.end(); ++iter )
      cout << *iter << endl;
    vector和deque的iterator也支持算数操作(iterator arichmetic),其它不太支持.
    iter + n ,iter - n , iter1 - iter2(difference_type,signed)
    vector<int>::iterator mid = ivec.begin() + ivec.end()/2 在vector的push_back操作后,当前iter的值失效.

  5,bitset类型
    是位操作的类模板. #include <bitset>  using std::bitset;
    初始化:   bitset<n> b;     bitset<n> b(u); ulong的位副本 
  bitset<n> b(s);string中位串的副本  bitset<n> b(s,pos,n);string中pos后n个位的副本
  bitset<32> bitset(0xffff);
  string strval("1100");    bitset<32> bitset(strval);
    操作: b.any(); b.none(); b.count() b.size() b[pos]
  b.test(pos); b.set(); b.set(pos) b.reset() b.rest(pos)
  b.flip();取反 b.flip(pos); b.to_ulong();返回ulong值 os << b;
    输出二进制: cout << "bitvec" << bitvec << endl; 

STL的输入输出:c++的输入输出由标准库提供.1,文件和控制窗口的IO库 2,定义了一些类(istream,ostream),使string能像文件一样操作.
  istream ostream cin cout cerr >> <<  getline
 1,标准库
  ostream -> ofstream
    -> ostringstream
    ----------------->/ iostream -> stringstream (字符串操作流) char型组成的流
  istream ----------------->/     -> fstream  (文件操作流) char型组成的流

    -> ifstream
    -> istringstream
  
  wostream,wistream,wiostream,wistringstream,wostringstream,wstringstream,wcin,wcout,wcerr.
   加w支持wchar_t型(国际宽字符)组成的流. 
  iostream流不支持复制和赋值操作.只能用指针或引用.
   ofstream &print(ofstream&);
   while (print(out2)) { /* ... */ }
 2,条件状态成员
  strm::iostate  strm::badbit  strm::failbit  strm::eofbit
  s.eof() s.fail() s.bad() s.good() s.clear() s.clear(flag) 
  s.setstate(flag) s.rdstate()
  所有流对象包含一个int型iostate数据成员,其定义了3个常量值,分别定义特定的位模式.badbit,failbit,eofbit/failbit.
  int ival;
  while ( cin >> ival, !cin.eof() ) {    判断非空
   if ( cin.bad() )      流坏了
    throw runtime_error("IO stream corrupted");
   if ( cin.fail() ) {      流出错
    cerr << "bad data, try again";
    cin.clear( istream::failbit );   清除出错标志
    continue;
   }
  }

  istream::iostate old_state = cin.rdstate();   读取条件状态
  cin.clear();
  process_input();
  cin.clear(old_state);

  is.setstate( ifstream::badbit | ifstream::failbit );  多状态处理

 3,输出缓冲区的管理
  

 4,文件的输入输出

 5,字符串流
 

第二部分:标准库的容器与算法
顺序容器
  1,定义
 按位置存取单一元素.容器的接口应该统一,标准库只提供少量接口,大部分由算法库提供,分3种:
    1,适用于所有容器的统一接口.
    2,只适用于顺序或关联容器的接口   
    3,只适用于顺序或关联容器的子集(部分容器)的接口
 标准库提供了3种顺序容器
   vector: 支持快速随机访问
   list:  支持快速插入/删除
   deque(double end queue): 双端队列
  和3种adapter(在原始接口上定义出的新接口)  
   stack  后进先出LIFO的栈
   queue  见进先出FIFO的队列
   priority_queue 有优先级管理的队列
 初始化:     #include <vector>  #include <list>  #include <deque>
  1,定义     vector<string> svec;  list<int> ilist;  deque<Sales_item> items
      2,定义为另一个容器的副本  vector<int> ivec2(ivec);
  3,定义并初始化为一段元素的副本 list<string> slist( svec.begin(), svec.end() ); 利用迭代器
       vector<string>::iterator mid = svec.begin() + svec.size()/2;
       deque<string> front(svec.begin(), mid);
       deque<string> back( mid, svec.end() );
       list<sting> word2( words, words+word_size ); 指针就是迭代器
  4,初始化指定数目的元素  list<int> ilist(list_size);
       vector<string> svec( get_word_count("Chimera") );
 元素的要求:
  1,可复制和赋值,内置和复合类型;容器;标准库类型可以做元素.
      引用和IO库类型不能做元素.
  2,有些容器操作要求有指定容器大小和单个初始化的构造函数,大部分的元素类型有这个默认的构造函数.
   例如foo没默认的构造函数,但定义了一个int形参的构造函数,则可以 vector<foo> ok(10,1);
  3,可以用容器作为元素. vector< vector<string> > lines; 注意> >

  2,迭代器
 vector和deque迭代器多了支持+,-,>=,<=.list只支持++,--,==,!=
       容器的insert,earse操作会使iterator无效.
 
  3,容器中的操作
 类型别名:  size_type;iterator;const iterator(只读);const_reverse_iterator;difference_type(存储2个it差值,带符号int);
    value_type(元素类型);reference(元素的左值类型value_type&);const_reference
 begin和end成员: c.begin();c.end();c.rbegin();c.rend();(返回逆序it)
 添加元素:  c.push_back(T);    在尾部插入一个元素,void,会导致迭代器失效,所以不能存储v.end()到变量.
    c.push_front(T);    在前部插入一个元素.
    c.insert(pos,T);    指定位置添加
    c.insert(pos,n,T);
    c.insert(pos,pos1,pos2);
    string sarray[4] = {"quasi","simba","frollo","scar"};
    slist.insert(slist_iter, sarray+2,sarray+4);
 删除元素:  c.earse(it); c.earese(b,e);(返回下个it) c.clear(); c.pop_bak(); c.pop_front(); (void)
 关系操作符:  ==; !=; ++; >=; <=
 大小操作:  c.size(); size_type 
    c.max_size(); size_type
    c.empty(); bool是否空
    c.resize(n); 改变容器大小
    c.resize(n,t);改变大小并初始化为t.
 访问元素:  c.front();返回引用 c.back(); c(n);下标访问 c.at(n);也是返回下标n的元素引用
    iterator解引用  *it
 赋值与swap:  c1=c2;   赋值后容器长度会相等
    c1.swap(c2);  c1和c2互换
    c1.assign(b,e) 将其他容器的b到e元素复制到c1
    c1,assign(n,t) 将c1设置为n个t    
  4,vector自增长时内存分配: vector容器是连续存放在内存里的,超界时要申请新空间和copy.影响效率,list和deque好些,但在空间类vector增长效率最好.
    ivec.capacity(); 返回现有空间
    ivec.reserve(50); 将空间设为50,STL以加倍策略为vector分配新的存储空间.
  5,容器的选用
 list不连续,不支持随机访问,高效insert和erase,单个访问慢点.
 vector连续,可随机访问,insert和erase要移动右边元素慢,单个访问快.
 deque两端insert和erase快,中间insert和erase代价更高,可随机访问.
     原则: 1,随机访问用vector和deque.
  2,随机插入用list.
  3,头尾插入用deque.
  4,list输入,复制到vector并排序,随机读出.
  5,综合考虑   
  6,string类型
 可以看做vector,其大部分操作和顺序容器一样.不支持栈方式:fron,back,pop_back等,好多,先列纲;
 1,构造和初始化
 2,操作
  插入删除;
   vector的
   特有的
  substr
  append
  replace
  find
  compare
  7,容器适配器
 adaptor是一种事物的行为类拟另一种事物的行为的一种机制.容器适配器是容器的扩展.
 标准库提供三种顺序容器适配器;
  queue(FIFO先入后出),priority_queue(按优先级存入,按级取出),stack(栈). 例如:stack适配器可以使顺序容器以栈方式工作.
 #include <stack>  stack,queue基于deque(双端队列)实现,priority_queue基于vector
 #include <queue>  预定义: size_type;value_type;container_type
 stack<int> stk(deq);      定义并初始化
 stack<string, vector<string>> str_stk(svec);  指定第二实参vector,可以覆盖原来的deque基本容器类型
  stack可以基于vector,list,deque;queue要求push_front()只能基于list;priority_queue要求随机访问只能基于vector和deque.
 容器适配器可以: ==  !=  <  <=  >  >=
    stack操作:  s.empty() s.size() s.pop()弹出不返回 s.top()返回不弹出 s.push(item)压栈
    queue/p_queue操作: q.empty() q.size() q.pop() q.top q.push(item) q.front() q.back()   

关联容器(associative container),关联容器按"键"值存取和查找,顺序容器按位置.操作差不多.
  1,pair(一对) 类型:包含2个数据值.first .secnod的集合
 pair<T1, T2>       定义pair,其内有T1,T2类型
 pair<T1, T2> p1(v1, v2);     创建pair,定义其包含T1,T2类型,并初始化为v1,v2
 make_pair(v1, v2)      用v1,v2创建pair对象
 p1 < p2       按字典顺序顺序比较
 p1 == p2       顺序比较pair里每个元素相等
 p.first / p.second      返回第一,第二个成员
  2,操作:
 关联容器的大部分操作和顺序容器一样,但也有不同.
    不同点:

  3,map类型:key-value成队出现的集合,key是索引.字典里单词是键,解释就是注释.key必须有一个比较函数<定义在容器里,
            此比较函数必须strick weak ordering.
 map<k, v>    m;    定义一个map类型
 map<k, v>   m(m2);    创建m2的副本
 map<k, v>   m(b, e);   创建迭代器b到e的副本到m,迭代器指向的元素必须能转换为pair<k, v>
 
 map<k, v>::value_type   是一个pair类型, pair<const map<k,v>::key_type, map<k,v>::mapped_type>
 typedef map<k, v>::key_type  key_type  定义类型别名
 typedef map<k, v>::mapped_type mapped_type

 map的迭代器解引用是指向一个pair型的值. 
 map<string, int>::iterator map_it=m.begin();
 map_it->first = "new key";
 ++map_it->second;
    操作:
 1,添加:  map::insert
 2,下标访问: 
  map<string,int> word_count;
  word_count["Anna"] = 1;
 3,查找:  map.count(key);map.find(key)
 4,删除:  map.erase(key);返回删除的size_type个数
   map.erase(p); iterator p  void
   map.erase(b,e); iterator b,e  void
 5,迭代遍历: for() cout<<map_it->first<<map_it->second<<endl; 分别输出
 6,实例:  单词转换

  4,set类型:key键的集合,key是const的.通过键快速读取.
 将某段元素或插入一组元素到set,实际只是添加了一个key.
 set< string > set1;
 set1.insert("the");
 set1.insert(ivec.begin(),ivec.end());
 iset.find(5);  返回iterator或iset.end()
 iset.count(5); 返回1,没找到返回0

  5,multimap和multiset:一个键对应多个实例.
 插入: authors.insert(make_pair(sting("Barth,John"),string("Sot-Weed Factor")));
  authors.insert(make_pair(sting("Barth,John"),string("Lost in the Funhouse"))); 1对多插入
 删除: multimap<string,string>::size_type cnt = authors.erase(("Barth,John"); 删除key对应所有元素,返回个数.删除迭代器只删除指定元素
 查找: 以key打头,元素相邻存放.
  1,使用 find(key)和count(key): iter=authors.find(search_item);
       cout<<iter->second<<endl;
  2,使用 lower_bound(key),upper_bound(key)     返回iter
  3,使用 equal_range(key);      返回iter型pair

  6,容器的综合应用,文本查询程序.

泛型算法:generic algorithm
  1,概述:标准库为容器提供了基本操作的功能函数,还定义了泛行算法来进行排序,查找等工作.标准库有超过100种算法.
   算法一般使用迭代器遍历来实现,其元素可以比较.
 #include <algorithm>    泛型算法
 #include <numeric>    一组泛化的算术算法
  2,初窥算法
 1,只读算法
  find(vec.beging(), vec.end(), search_value);
  string sum=accumulate(vec.begin(), vec.end(), string(" ")); vec里的元素强制转换或匹配" "类型,既const char*型并相加
  it = find_first_of( roster1.begin(),roster1.end(), rester2.begin(),rester2.end() ); 
           在第二段中找出和第一段里匹配的任一元素,返回其iter
 2,写
  fill(vec.begin(), vec.begin()+vec.size()/2, 10);  会先检查空间再写入
  fill_n(vec.begin(), 10, 0);      不检查强制写入
  fill_n(back_insert(vec), 10, 0);    back_insert(vec)插入迭代器是迭代器适配器,要带一个实参指定类型
           这里实参是一个容器的引用.
  copy( ilst.begin(), ilst.end(), back_inserter(ivec) );  复制一段元素到目标,这个例子效率差
  vector<int> ivec(ilst.begin(),ilst.end());   直接初始化会快点
  replace(list.begin(), list.end(), 0, 42);    将一段元素里的0替换为42.
  replace_copy(list.begin(), list.end(), back_insert(ivec), 0, 42); 先copy一个ilst副本到ivec,且所有0变成42. 
 3,排序实例:统计文章里的长度大于6的单词的个数
  bool isShorter( const string &s1, const string &s2 )
  {
   return s1.size() < s2.size();
  }
  bool GT6( const string &s )
  {
   return s.size() >= 6;
  }
  int main()
  {
   vector<string> words;
   string next_word;
   while ( cin>> next_word ) {
    words.push_back(next_word);      插入到容器
   }
   sort( words.begin(), words.end() );      按字典排序
   vector<string>::iterator end_unique = unique(words.begin(), words.end()); 将重复的放后面
   words.erase( end_unique, words_end() );
   stable_sort( words.begin(), words.end(), isShorter );长短排序,isShorter谓词函数,2个元素类型实参,返回可做检测条件的值. 
   vector<string>::size_type wc = count_if( words.begin(), word.end(), GT6 );统计GT6个数
   cout << wc << make_plural(wc, "word", "s") << "6 charactor or longer" << endl;
   return 0;
  }
  
  3,再谈迭代器:#include <iterator.h>还定义了其他迭代器,insert iterator,iostream iterator,reverse iterator.
 1,插入迭代器
  带有3种迭代器适配器:back_inserter;front_inserter;inserter.
  replace_copy( ivec.begin(), ivec.end(), insert(ilst, it), 100, 0 );  复制ivec到ilst的it位置,100换为0
 2,iostream_iterator流迭代器:是类模版,所有带<<,>>操作符的类都可以使用.
  定义了的构造函数:
  istream_iterator<T> in(strm);  strm是关联的输入流
  istream_iterator<T> in;
  ostream_iterator<T> out(strm);
  ostream_iterator<T> out(strm, delim); 写入时用delim做元素的分隔符,delim是以空字符结束的字符数组,也就是字符串.
  定义了的操作:自增,解引用,赋值和比较
  ++it;it++;*it;it->mem;it1==it2;it1!=it2
  1,输入 istream_iterator<int> cin_it(cin);
   istream_iterator<int> eof        定义结束符
   while( in_iter != eof ) vec.push_back(*in_iter++);  
  2,输出 vector<int> ivec(in_iter, eof);
   ostream_iterator<string> out_iter(cout, "/n");
   istream_iterator<string> in_iter(cin), eof;
   while( in_iter != eof ) 
    *out_iter++ = *in_iter++;
  3,类做流
   istream_iterator<Sales_item> item_iter(cin), eof;
   Sales_item sum = *item_iter++;
   while ( item_iter != eof ) {
    if ( item_iter->same_isbn(sum) ) sum = sum + *item_iter;  比较ISBN,相加
    else {
     cout << sum << endl;
     sum = *item_iter; }
    ++item_iter;
   }
   cout << sum << endl;
  4,和算法一起使用
   istream_iterator<int> cin_it(cin), eof;
   vector<int> vec(cint_it, eof);
   sort( vec.begin(), vec.end() );
   ostream_iterator<int> output( cout, " " );
   unique_copy( vec.begin(), vec.end(), output );    不重复拷贝
 3,反向迭代器reverse_iterator
  vector<int>::reverse_iterator r_iter;
  begin()是最后一个,end()是第一个的前一个.sort(ivec.rbegin(), ivec.rend())将由大到小排序.
  string::reverse_iterator rcomma = find( line.rbegin(), line.rend(), ',' );  rcomma指向','号
  cout << string(rcomma.base(), line.end()) << endl;     rcomma的成员函数base()指向LAST
 4,const迭代器
  const后不能用这个迭代器来修改容器里的元素.
  find_first_of( it, roster1.end(), roster2.begin(), roster2.end() ) 这里不用const是因为it必须roster1.end()同类型
     roster1.end()返回的迭代器依赖于roster1类型.如果roster1是const对象,则迭代器是const_iterator.
 5,按算法要求的功能分五种迭代器
  输入迭代器:input  只读,自增  ,== * -> ,find accumulate ,istream_iterator
  输出迭代器:output  只写,自增  ,*  ,copy   ,ostream_iterator
  前向迭代器:forward  读写,自增  ,  ,replace
  双向迭代器:bidirectrional 读写,自增自减  ,--  ,reverse  ,map,set,list自带迭代器
  随机迭代器:random-access 读写,完整的算术操作 ,< <= += iter[n] ,sort  ,vector,deque,string自带迭代器.
  4,泛型算法的结构,100多种算法分类
 按形参模式
  alg ( beg, end, other, parms );
  alg ( beg, end, dest, other parms );
  alg ( beg, end, beg2, other parms );
  alg ( beg, end, beg2, end2, other parms );
 命名规范
  sort( beg, end );
  sort( beg, end, comp );
  find( beg, end, val );
  find_if( beg, end, pred );
  reverse( beg, end );
  reverse_copy( beg, end, dest );
  5,容器特有的算法
 因为list是双向连续而不随机的,不能sort,merge,remove,reverse,unique等标准库算法也效率不高.所以标准库定义其他算法给list.
 它们用在其他容器上有相同效果.
  lst.merge(lst2); lst.merge(lst2, comp);
  lst.remove(val); lst.remove_if(unaryPred);
  lst.reverse()
  lst.sort()
  lst.splice(iter, lst2) lst.splice(iter, lst2, iter2) lst.splice(iter, beg, end)
  lst.unique()  lst.unique(binaryPred)
第三部分:类Class
类Class
  1,Class的声明,定义和类对象
 class Screen;      声明
 class Screen{      定义类类型,定义了才能固定成员,预定存储空间.
 public:       类成员类型:public private protected
  typedef std::string::size_type index;  定义类型别名来简化类,并允许客户使用

  char get() const { return contents[cursor]; }; 成员函数.默认为inline
  inline char get(index ht, index wd) const; 可以重载,可以显式的定义为inline(类定义体内部),const是将成员函数定义为常量
  index get_cursor() const;

  Screen *next;     有了类的声明,类成员就可以是自身类型的指针或引用.
  Screen *prev;

  screen(): units_sold(0),revenue(0.0) { } 构造函数是与类同名的成员函数,用于对每个成员设置初始值.
 private:       
  std::string contents;    多个数据成员的私有化体现类的抽象和封装
  index cursor;
  index height,width;
 protected:
 };        右花括号,类定义结束,编译器才算定义了类.
 char Screen::get(index r, index c) const   这是成员函数的定义,Scrren::后形参表和函数体是在类作用域中,
         可以直接引用类定义体中的其它成员.而返回在Screen::之前,要Screen::index使用.
 {
  index row = r * width;
  return contents[row + c];
 }

 inline Screen::index Screen::get_cursor() const  或在类定义体外部指定inline,阅读方便
  return corsor;
  
 Screen screen;      定义一个类类型的对象时才分配存储空间
 class Screen screen;

  2,this指针:对象到成员函数,包含一个隐含形参this,编译器会定义,成员函数可以显式的引用this指针.
 class Screen
 {
  public:
   Screen& move( index r, index c );
   Screen& set( char );
   Screen& set( index, index, char );

   Screen& display( std::ostream &os )
     { do_display(os); return *this }
   const screen& display( std::ostream &os ) const 
         在const成员函数中,this指针是指向const类对象的const指针.内容地址都不能变
     { do_display(os); return *this }
   ...
  private:
   void do_display( std::ostream &os ) const 私有成员函数,放在这简捷,好调试,好改.
     { os << contents; }

   mutable size_t access_ctr;    mutable声明可变数据成员,在const对象和函数里,数据都可改变
  protected:
 };
 Screen& Screen::move( index r, index c )  本对象的引用 
 {
  index row = r * width;
  cursor = row + c;
  return *this;     返回this指针的内容
 }
 Screen& Screen::set( char c )
 {
  contents[cursor] = c;
  return *this;      普通成员函数里,this指针是const指针,能改内容但不能改地址.
 }
 
 Screen myscreen;
 myscreen.move(4,0).set('#').display(cout);  操作序列的使用.call non-const ver
 myscreen.move(4,0).display(cout).set('#');  对于const的display成员函数,这个操作违法.所以使用重载.
 const Screen blank.display(cout);    call const ver

  3,作用域
 每个类都有自己的作用域,成员一样的也是不同的域.
 obj.member  obj.memfun()
 ptr->member  ptr->memfun()
 类作用域中名字的查找:块内->类定义体内->全局,(都是查找在名字使用前的定义(块内没找到会到类定义全局里找),不能重复定义.在名字后重复定义也不行)

  4,构造函数
 包括名字,形参表,初始化列表和函数体.
 重载: class Sales_item
  { 
   public:
    Sales_item(); 
    Sales_item( const std::string& ); 参数不同,不能定义为const
    Sales_item( std::istream& );
   ...
  }
  Sales_item empty;
  Sales_item Primer_3rd_Ed( "0-201-138-178" ); 实参决定使用哪个构造函数
  Sales_item Primer_4th_Ed( cin );
 初始化:
  1,screen(): units_sold(0),revenue(0.0) { } 构造函数是与类同名的成员函数,用于对每个成员设置初始值.
  2,Sales_item::Sales_item( const string &book )
    {
   isbn = book;     在函数体内初始化.
   units_sold = 0;    isbn是隐形的初始化,使用string默认的构造函数初始化.
   revenue = 0.0;
    }
  3,对于const数据对象和引用,只能而且必须初始化,不能在函数体里赋值.
  4,初始化可以是任意表达式.
  5,类类型的初始化可以是类的任一构造函数. Sales_item():isbn(10,'#'),.. { }
  6,Sales_item( const std::string &book = " " ):isbn(book),units_sold(0),revenue(0.0) { }
    Sales_item empty;     empty中包括一个默认实参.
    Sales_item Primer_3rd_Ed( "0-201-123-189" ); 显式实参.
  7,默认的构造函数
   当类没定义构造函数时,编译器使用默认的构造函数.即类成员用各自的默认构造函数来初始化.
   内置和复合类型成员(指针和数组),编译器只对定义在全局作用域的对象初始化.定义在局部的,不进行初始化.必须使用构造函数.
  8.隐式类类型转换
   string null_book = " 9-999-999-9 ";
   item.same_isbn( null_book );  same_isbn()期待一个Sales_item对象做实参. string->构造函数->临时的Sales_item对象
    如果把构造函数声明为explicit Sales_item( const std::string &book = " " ): 则关闭隐式转换.
  9.显式类类型转换
   item.same_isbn( Sales_item(null_book) ); 关闭explicit功能
  10.显式初始化没有定义构造函数且全部public的类.但程序员工作量大且删除成员要改程序.
   vall.ival=0; vall.ptr=0;    

  5,友元
 class Screen{
  friend class window_Mgr; 定义友元类,友元(成员函数,非成员函数或类)可以访问Screen类中的所有成员,包括私有成员.
  ...
 }
 class Screen{
  friend window_Mgr& window_Mgr::relocate(  Screen::index r, Screen::index c, Screen& s ); 其它类的成员函数成为友元
  ...
 }
 window_Mgr& window_Mgr::relocate( Screen::index r, Screen::index c, Screen& s )
 {
  s.height += r;
  s.width += c;
  return *this;
 }
 成员函数做友元要先定义。
 非成员函数和类不必先声明,用友元引入的类名和函数,可以象预先声明样使用.
 重载的函数要单独声明为友元.

  6,static 成员
 1, 声明类的静态成员与类关联,但不与类的对象关联,没有this,不能做虚函数.便于封装.
 2, 静态成员的提出是为了解决数据共享的问题.实现共享有许多方法,如:设置全局性的变量或对象是一种方法。但是,全局变量或对象是有局限性的。
 在类中,静态成员可以实现多个对象之间的数据共享,并且使用静态数据成员还不会破坏隐藏的原则,即保证了安全性。因此,静态成员是类的所有对象中
 共享的成员,而不是某个对象的成员。
   3, 使用静态数据成员可以节省内存,因为它是所有对象所公有的,因此,对多个对象来说,静态数据成员只存储一处,供所有对象共用。
 静态数据成员的值对每个对象都是一样,但它的值是可以更新的。只要对静态数据成员的值更新一次,保证所有对象存取更新后的相同的值,
 这样可以提高时间效率。

 const int account::period;     必须做 const static 的类外定义
 double account::interestRate = initRate();   类外定义并初始化,可以直接引用私有成员.
 class account{
 public:
  void applyint() { amount += amount*interestRate }; 类里直接引用
  static double rate() { return interestRate; }
  static void rate( double );
 private:
  std:string owner;
  double amount;
  static double interestRate;
  static double initRate;

  static const int period = 30;    const static 的类内初始化.
 };
 void account::rate( double newRate )    这里不用加static,类外定义了.
 {
  interestRate = newRate;
 }

 2.static 成员不是类的组成部分,他的特殊用法和成员不一样
 class bar {
 public:
  Bar& clear( char = bkground );
 private:
  static const char bkground = '#';    static数据成员做默认实参
  static bar mem1;      定义为该类的类型
 }

其他
  7,复制构造函数
 类定义体里的为默认构造函数
  ifstream file1( "filename" );    直接初始化
  Sales_item item = string( "9-999-99999-9" );  =复制操作符,Sales_item构造函数不是显式的(explicit)
  string make_plural( size_t, const string&, const string& ); const类的引用做形参,隐式的使用string复制构造函数做实参.返回复数形式
  vector<string> svec(5);     先用string默认构造函数初始化svec,再使用复制构造函数复制到svec每个元素.
  Sales_item primer_eds[] = {
      string("0-201-16487-6"), 调用单实参构造函数
      string("0-201-54848-8"),
      string("0-201-82470-1"),
      Sales_item()   调用完整的构造函数语法
      };
 如果没有定义复制构造函数,编译器自动合成一个,内容是对逐个成员初始化.
 定义复制构造函数
  class foo {
    public:
     foo();     默认的构造函数
     foo(const foo&);   声明复制构造函数,主要用于指针成员处理和消息处理.
   };
 把复制构造函数定义在private区域,可以禁止复制,如果再只声明不定义,则友元和类成员中的复制也禁止.

  8,赋值操作符 =
 =操作符的重载
 class Sales_item {
  public:
   Sales_item& operator=(const Sales_item &); 在类里要声明,左参数带this,右参数用const,返回右参数的引用.
 };
 用户没定义=操作符重载,则编译器会自动合成赋值操作符.复制构造函数和赋值操作符重载通常一起定义.
 Sales_item& Sales_item::operator=( const Sales_item &rhs )
 {
  isbn = rhs.isbn;
  units_sold = rhs.units_sold;
  revenue = rhs.revenue;
  return *this;
 }

  9,析构函数
 构造函数申请资源(buffer或open file),析构函数就要释放资源(buffer或close file).
 Sales_item *p = new Sales_item; p指向一个新Sales_item对象,调用默认构造函数
 {
  Sales_item item(*p);  *p复制到item
  delete p;   删除指针p,调用析构函数释放p资源
 }     超出作用域,调用析构函数释放item

 {
  Sales_item *p = new Sales_item[10]; 动态分配
  vector<Sales_item> vec( p, p+10 );  本地对象
  ...
  delete [] p;     删除p
  ...
 }       vector默认析构函数,逐个逆序撤销元素(size()-1,-2,..0)
 复制构造函数和赋值操作符重载,析构函数 通常一起使用.
 编译器合成的析构函数按对象创建的逆序撤销每个非static成员.
 class Sales_item{
  public :
  ~Sales_item();  用户编写的析构函数,没形参,没返回.撤销本对象时会调用,再调用合成的析构函数.
  { }
 };

  10,例子消息处理,将信息字符串message加入关联容器set:folder里
 class message{
 public:
  message( const std::string &str = "" ):contents(str) { } 默认构造函数,单个实参(空串)并将folder初始化为空集
  message( const message& );      复制构造函数声明 
  message& operator=( const message& );    赋值=操作符重载
  ~message();        析构
  void save( folders& ); 
  void remove ( folder& );
 private:
  std::string contents;
  std::set<folder*> folders;
  void put_msg_in_folders( const std::set<folder*>& );  各个folder添加指向msg的指针,复制和赋值将调用这个函数  
  void remove_msg_from_folders();     各个folder删除指向msg的指针,析构和赋值将调用这个函数
 };
 message::message( const message &m ):content(m.contents),folders(m.folders) 用副本初始化
 {
  put_msg_in_folders( folders );     复制构造函数要把msg添加到folders
 }
 void message::put_msg_in_folders( const set<folder*> &rhs )
 {
  for( std::set<folder*>::const_iterator beg=rhs.begin(); beg!=rhs.end(); ++beg ) 利用迭代器遍历
    (*beg)->addmsg(this);      *beg是folder指针,利用addmsg()成员函数实现添加
 }
 message& message::operator=( const message &rhs )   赋值定义
 {
  if ( &rhs != this )       左右操作数不同 
  {
   remove_msg_from_folders();     从folders删除msg
   contents = ths.contents;
   folders = rhs.folders;
   put_mag_in_folders(rhs.folders);    将新msg加入folders
  }
  return *this;
 }
 void message::remove_msg_from_folders()
 {
  for ( std::set<folder*>::const_iterator beg=folders.begin(); beg!=folders.end; ++beg )
   (*beg)->remmsg(this);
 }
 message::~message()        析构函数
 {
  remove_msg_from_folders();
 }

  11,指针成员管理,3种方法
 1,常规指针成员
  class hasptr{
  public:
   hasptr(int *p, int i):ptr(p), val(i) { }   构造函数,2个形参
   int *get_ptr() const { return ptr; }
   int get_int const { retrun val; }
   void set_ptr( int *p ) { ptr = p; }
   void set_int( int i ) { val = i; }
   int get_ptr_val() const { retrun *ptr; }
   void set_ptr_val( int val ) const { *ptr = val; }
  private:
   int *ptr;       指针成员
   int val;
  };

  int obj = 0;
  hasptr ptr1( &obj, 42 );
  hasptr ptr2( ptr1 );       2个对象中的2个指针成员,共享同一对象obj 
  这时可能出现空指针,悬垂指针.
  int *p = new int(42);
  hasptr ptr( p, 10 );
  delete p;
  ptr.set_ptr_val(0);       空指针,*p空间已经删除了

 2,智能指针成员,smart point
  在析构函数中加计数器,当计数为0时,删除共享对象.避免空指针(悬垂指针).但这样在有3个引用时计数器无法同步.
  可以使用计数类(一个指针指到共享空间,一个计数)来解决这个问题:
  class U_Ptr {
  private:      所有成员都是private
   friend class HasPtr;    友元HasPtr
   int *ip;     指针
   size_t use;     计数
   U_Ptr(int *p):ip(p),use(1) { }  构造
   ~U_Ptr() { delete ip; }   析构
  };
  class hasptr{
  public;
   hasptr( int *p, int i ):ptr(new U_Ptr(p)),val(i) { }    构造
   hasptr( const hasptr &orig ):ptr(orig.ptr),val(orig.val) { ++ptr->use; } 复制
   hasptr& operator=( const hasptr& );      赋值
   ~hasptr() { if (--ptr->use == 0 ) delete ptr; }    析构

   int *get_ptr() const { retrun ptr->ip; }      操作成员函数
   int get_int() const { return val; }
   void set_ptr( int *p ) { ptr->ip = p; }
   void set_int( int i ) { val = i; }
   int get_ptr_val() const { return *ptr->ip; }
   void set_ptr_val( int i ) const { *ptr->ip = i; }
  private:
   U_Ptr *ptr;
   int val;
  };
  hasptr& hasptr::operator=( const hasptr &rhs )
  {
   ++rhs.ptr->use;    右操作数use++
   if ( --ptr->use == 0 )   左操作数use--为0,则左操作数被替换,先删除ptr.
    delete ptr;
   ptr = rhs.ptr;
   val = rhs.val;
   return *this;
  }

 3,值形指针成员
  不再使用共享数据,复制时把原指针的值copy到新的对象.
  class hasptr{
  public;
   hasptr( const int &p, int i ):ptr(new int(p)),val(i) { }   构造
   hasptr( const hasptr &orig ):ptr(new int(*orig.ptr)),val(orig.val) { } 复制
   hasptr& operator=( const hasptr& );      赋值
   ~hasptr() { delete ptr; }        析构

   int *get_ptr() const { retrun ptr; }      操作成员函数
   int get_int() const { return val; }
   void set_ptr( int *p ) { ptr = p; }
   void set_int( int i ) { val = i; }
   int get_ptr_val() const { return *ptr; }
   void set_ptr_val( int i ) const { *ptr = i; }
  private:
   int *ptr;
   int val;
  };
  hasptr& hasptr::operator=( const hasptr &rhs )
  {
   *ptr = *rhs.ptr;    直接把值copy到新对象,2个指针2个空间
   val = rhs.val;
   return *this;
  }

重载 操作符
  1,定义
  2,输入和输出操作符
  3,算数和关系操作符
  4,赋值操作符
  5,下标操作符
  6,成员访问操作符
  7,++,--
  8,调用 操作符和 函数对象
  9,转换 与 类 类型

第四部分:OOB编程与模板
OOB
  1,概述
 继承,动态绑定和数据抽象的概念是OOB的基础.
 模板(泛型类,泛型函数)定义了系列操作,用来定义具体类,他们都使用类似操作.
 1,c++的派生类(derived class)继承基类(base class),base class中成员函数分 非虚函数std::string book();
   虚函数virtual double net_price(size_t).虚函数(virtual)需要在派生类里重定义.
 2,动态绑定(dynamic banding)是指应用程序会根据实参调用的继承层次中相应类 的虚函数 来执行,形参是基类的引用(或指针)const Item_base &item.
  2,基类和派生类
 派生类和用户程序一样只能访问基类的public,不能访问private.友元可以访问private.
 为了虚函数在派生类里的重定义,虚函数用到的数据成员定义到protected,它们可以通过派生类的继承关系访问.
 但不能在引用里直接访问基类对象的protected成员.
 class Bulk_item;      前向声明
 class Item_base;      
 class Bulk_item : public Item_base{   Item_base必须先定义
  public:
   double net_price(std::size_t) const; 虚函数的声明必须和基类里一样,但返回可以是基类或派生类的引用(指针).
  private:
   std::size_t min_qty;
   double discount;
 };
 派生类包括的数据成员:min_qty,discount/isbn,price.
 double Bulk_item::net_price( size_t cnt ) const
 {
  if ( cnt >= min_qty )
   return cnt*(1-discount)*price;  派生类里对基类protected数据成员的直接调用.
  else
   return cnt*price;    卖了几本书,计算总价(包括折扣)
 }
 1,可以将基类的引用绑定到继承类,也可以用基类的指针指向派生类.
   double print_total( const item_base&, size_t );
   print_total( bulk, 10 ); Item_base *p=&bulk;
 2,根据实参的不同,调用不同的虚函数版本.
   void print_total( ostream &os, const item_base &item, size_t n )  引用和指针的静态类型和动态类型的多态性是c++的基石
   {
  os << "ISBN" << item.book() << item.net_price(n) << endl;  不过实参是什么item.book非虚函数都是item_base里的.
            item.net_price(n)虚函数是可变的.
   }
   print_total( cout, base, 10 );
   print_total( cout, derived, 10);
 3,强制某个虚函数.
   Item_base *baseP = &derived;
   double d = baseP->Item_base::net_price(42);   强制使用基类的虚函数,因为继承层次的基类中一般包含公共信息.
 4,虚函数也有默认实参,在基类和派生类里会有不同的默认实参,如果通过基类的引用和指针做参数,实参是派生类,这时默认参数是基类的,不是派生类的.
      5,继承类型:
  struct Public_derived : public Base{}    派生类继承基类public和protected
  struct Protected_derived : protected Base {}   到派生类里都为protected
  struct Private_derived : private Base {}    到派生类里都为private
 6,恢复个别成员的访问级别
   class Derived : private Base {     基类里所有成员继承为私有级别
  public :
   using Base::size;     使用using恢复访问级别,但改变不了原级别
  protected :
   using Base::n;
  //...
 };
 7,struct D2 : Base {};      默认继承级别是public 
   class D3 : Base {};      默认继承级别是private
 8,友元(friend class Frnd;)可以访问类的private和protected,但不能访问继承类的.
 9,类的静态成员访问 static void statmem();继承层次里只能有一个静态函数.它根据访问级别被访问.可以用 :: -> .
   Base::statmem();
   Derived::statmem();
   derived_obj.statmem();
   statmem();

  3,转换及继承(比较难理解)
      派生类->基类 
 1,派生类对象引用 -> 基类对象的引用:只是引用,派生类没变化.
 2,派生类对象 -> 基类对象 :会复制派生类的基类部分到实参.
  Item_base item;
  Bulk_item bulk;
  Item_base item(bulk); 复制,bulk(item_base部分)的引用做实参->item_base的复制构造函数(初始化)
  item = bulk;  赋值,bulk(item_base部分)的引用做实参->item_base的赋值函数(赋值)
 3,派生类到基类的可访问性,参照基类的public部分能否访问.
  public继承, 用户和后代类    可以用此派生类访问基类.
  private继承,用户和后代类都不可以.
  protect继承,后代类可以,用户不可以.
  派生类里的成员都可以使用 派生类到基类的装换.
      基类->派生类是不合理和不存在的.但指针可以绑定.
  Item_base *itemP = &bulk;  指针可以绑定,编译器会报错.
  static_cast  Item_base *itemP = &bulk;   强制转换
  dynamic_cast Item_base *itemP = &bulk;  申请运行是检查
 
  4,构造函数和复制控制
      构造函数
 派生类也有构造,复制,赋值,撤销.在继承层次中基类要考虑其构造函数是protected或private以保证派生类使用其特殊构造函数.
 默认的派生类构造函数先默认初始化基类,再默认初始化其数据成员.
 用户定义的构造函数可以传递实参. Bulk_item bulk("0-201-82470-1", 50, 5, .19);
 class Bulk_item : public Item_base {
 public:
  Bulk_item( const std::string book="", double sales_price=0.0, std::size_t qty=0, double disc_rate=0.0 ):默认参数
       Item_base(book, salse_price),min_qty(qty),discount(disc_rate) { }   初始化
 };
 这里的实参只能初始化到直接的基类数据.
 重构refactoring的概念,例如在2层间加入一层以便扩展或处理其他改变时,使用上下二层类的程序不用改变,但要重新编译.
 首先要定义新层的类:
 class Disc_item : public Item_base {
 public:
  Disc_item(const std::string book=" ",double price=0.0, std::size_t qty=0, double dis_rate=0.0):
       Item_base(book,price),quantity(qty),discount(dis_rate){ }
 protected:
  std::size_t quantity;
  double discount;
 }
 class Bulk_item : public Disc_item {
 public:
  Bulk_item(const std::string book=" ",double price=0.0, std::size_t qty=0, double dis_rate=0.0):
       Disc_item(book,price,qty,dis_rate) {}
  double net_price(std::size_t) const;
 protected:
 }
    复制控制
 1,复制
  class Derived : public Base{
  public:
   Derived( const Derived& d ):Base(d), , {}  派生类的复制构造函数,和构造函数不同.显式的定义了复制操作.
           必须包括Base(d)来调用Base的复制函数,如果不包括,
           派生类的基类复制部分将调用默认的构造函数.
   ...   
  }
 2,赋值
  Derived &Derived::operator=(const Derived &rhs)
  {
   if ( this != &rhs ) {
    Base::operator=(rhs);    基类赋值
    ...
   }
   return *this;
  }
 3,析构
  class Derived : public Base {
  public:
   ~Derived() { ... }     只析构派生类自己的成员,编译器会自动调用.
  };
 基类的虚析构函数,派生类将继承虚析构函数特性,不管是显式的还是合成的.空的虚析构函数不遵循三法则(可以不需要构造,复制,赋值等复制函数).
 构造和赋值操作符不能是虚函数.
 class Item_base {
 public:
  virtual ~Item_base() {}      空虚析构函数
 }
 在构造和析构函数中最好是不要包含虚函数,因为这个过程对象不固定.

  5,继承情况下的类作用域
 派生类的作用域嵌套在基类作用域中. cout << bulk.book();
 在派生类里找不到名字的定义,编译器就到下层类去找.重名的会使用上层的定义,可以用作用域操作符指定下层数据成员.
 struct Base {
   Base():mem(0){ }
 protected:
  int mem;
 };
 struct Derived : Base {
  Derived(int i):mem(i) { }
  int get_mem() { return mem }     返回先找到的mem
  int get_base_mem { return Base::mem; }    指定下层mem
 protected:
  int mem;
 };
 重名的成员函数也一样,编译器使用先找到的那个,如果参数不匹配,就报错.  d.Base::memfcn();
 派生类成员函数重载,一般using基类中的所有重载成员函数(因为覆盖),再重定义本类里要定义的函数.
 虚函数定义在基类,派生类应该使用同一原型(包括形参),这样才能通过基类的引用或指针访问到派生类.编译器将先在基类查找匹配的函数.
 1,确定函数调用的对象,引用或指针的静态类型.
 2,查找函数名,有上到下.
 3,找到名字,进行常规检查,参数是不是合法.
 4,合法就生成代码,虚函数且是引用和指针调用就根据实参类型调用动态特性的函数.

  6,纯虚函数pure virtual
 一些类里的继承的虚函数不需要重定义(因为不使用),可以将这个类里的虚函数定义为纯虚函数,这个类(叫做abstract base class,抽象基类)
 将不能创建对象,只能用于派生层次.
 class Disc_item : public Item_base {
 public:
  double net_price( std::size_t ) const = 0;   纯虚函数
 }

  7,容器和句柄类用于继承
 容器是单一对象的集合,基类和派生类可以一起放入容器,当派生类会被切掉只剩下基类部分,也可以把基类强制装换为派生类放入容器,但派生部分没初始化.
 唯一办法是将对象的指针放入容器,用指针要保证容器存在,对象就存在,容器消失,动态分配的对象要适当的释放.
 muitlset<Item_base> basket;   容器是base_item的集合
 Item_base base; Bulk_item bulk;
 basket.insert(base);
 basket.insert(bult);    bult剪切到base部分加入集合

 为了继承和OOB,C++必须使用引用(reference)和指针(pointer),以便访问动态绑定的函数.C++使用句柄类(handle或叫cover包装类)来存储管理基类指针.
    指针型句柄类
 用户使用:
  Sales_item item( Bulk_item("0-201-82470-1",35,3,.20) ); 用户绑定句柄类到Item_base继承层次的对象.
  item->net_price();       用户使用 * -> 操作符操作Item_base,
           用户不用存储和管理对象,由句柄类处理,操作是多态行为.
 定义: 
 class Sales_item {
 public:
  Sales_item():p(0),use(new std::size_t(1) ) { }   1,默认构造函数
  Sales_item( const Sale_item &i ):p(i.p),use(i.use) { ++*use } 2,复制构造函数
  Sales_item( const Item_base& );     3,接受Item_base的复制构造函数
  ~Sales_item() { decr_use(); }     析构
  Sales_item& operation=( const Sales_item& );   赋值函数声明
  const Item_base *operation->() const {    ->符号 重定义,Sales_item返回一个item_base*.
   if (p) return p;
   else throw std::logic_error("unbound Sales_item"); } 无效的Sales_item,向内核抛出错误信息
  const Item_base &operation*() {     *符号 重定义,Sales_item&返回一个引用 item_base&
   if (p) return *p
   else throw std::logic_errir("unbound Sales_item"); }
 private:
  Item_base *p;        1,指向item_base的指针
  std::size_t *use;       2,指向计数use的指针
  void decr_use()
   { if(--*use == 0) delete p; delete use }
 };
 Sales_item& Sales_item::operation=( const sales_item &rhs ) {  
   ++*rhs.use;        use是指针,*use是内容
  decr_use();
  p = rhs.p;
  use = rhs.use;
  return *this; 
 }
 为了支持未知类型的复制,要在基类开始定义虚函数clone,再在派生里重定义.
 class Item_base{
 public:
  virtual Item_base* clone() const
   { return new Item_base(*this); }
 };
 class Bulk_item{
 public:
  Bulk_item* clone() const      
   { return new Bulk_item(*this); }    虚函数的返回可以是派生类的指针或引用
 };
 Sale_itme::Sales_item( const Item_base &item ): p(item.clone()),use(new std::size_t(1)) { }
           这个复制构造函数初始化,指针指到实参对象并新建use=1.
 使用句柄类Sales_item和关联容器muiltset来实现继承层次中虚函数的统一操作:购物篮,
 包括添加卖出的书,获得某书的卖出数量和总的卖价(折扣前和折扣后的),首先定义muitlset的比较Sales_item的方法:
 inline bool
 compare( const Sales_item &lhs, const Sales_item &rhs )  multiset容器可以指定比较函数
 {
  return lhs->book() < rhs->book();     Sales_item=继承层次中的某个对象的引用
 }
 class Basket {
  typedef bool (*Comp)(const Sales_item&, const Sales_item&); 将Comp定义为函数类型指针,该函数类型和compare函数相匹配
 public:
  typedef std::multiset<Sales_item,Comp> set_type;  类型别名set_type
  typedef set_type::size_type size_type;    类型别名size_type
  typedef set_type::const_iterator const_iter;   类型别名const_iter
  Basket(): items(compare) { }     构造是容器items初始化,指定使用compare函数做容器对比函数
  void add_item( const Sales_item &item )    接受Sales_item对象引用将其加入multiset
    { items.insert(item); }
  size_type size( const Sales_item &i ) const   接受Sales­_item对象引用返回该类ISBN的记录数
    { retrun items.count(i); }
  double total() const;      计算总的卖价
 private:
  std::muitlset<Sales_item,Comp> items;
 };
 double total() const{
  double sum = 0.0;
  for ( const_iter iter=items.begin();     迭代器iter,是容器里定义的数据类型(指针),和下标用处差不多. 
       iter!=items.end();     但用下标的容器不多
       iter=items.upper_bound(*iter) ) {  容器的upper_bound,形参是iter解引用(Sales_item),返回下一类isbn的iter
   sum += (*iter)->net_price( items.count(*iter) ); *iter 获得Sales_item对象,*iter-> 获得句柄关联的Item_base对象指针.
           net_price形参size_t,实参items.count(*iter)
  }
  return sum;
 }

  8,文本文件查询的进阶设计:
     实现功能: 
 查询Daddy  查询非~(Alice)  查询或(hair|Alice)  查询与(hair&Alice)  复合查询((fiery&bird)|wind)
     类定义: 
 1,定义出四个查询类: WordQuery,NotQuery,OrQuery,AndQuery,它们是兄弟类,应该共享相同的抽象接口.所以定义出一个抽象基类Query_base,
   作为查询操作继承层次的根.Query_base->WordQuery    单操作数
        ->NotQuery
        ->BinaryQuery->AndQuery  双操作数,所以加个抽象类BinaryQuery表示两个操作数的查询
           ->OrQuery
 2,定义TextQuery类:  读指定文件,建立查询,查找每个单词.
 3,定义Query_base类: 用来表示查询操作的类型,需要两个纯虚函数,每个派生类重定义自己的操作.
  1,eval: 返回匹配的行编号集合,TextQuery对象做形参
  2,display: 打印给定对象的查询结果,ostream引用做形参
 4,定义句柄类Query: Query对象将隐藏继承层次,用户代码根据此句柄执行,间接操作Query_base对象.
    Query必须: 定义&操作符
          定义|操作符
          定义~操作符
          定义参数为string对象的构造函数,生成新的WordQuery 
 5,应用代码:  Query q = Query("fiery") & Query("bird") | Query("wind");
 6,过程:   生成10个对象,3个Wordquery,1个OrQuery,1个AndQuery,5个对应的句柄.

            /->Wordquey(fiery)
            /-->Andquery
    Query对象-->OrQuery      /->Wordquey(bird)
            /-->Wordquey(wind) 
 
    知道了这个对象树,操作就会沿着这些链接,比如调用q,则q.eval->OrQuery.eval->AndQuery.eval ...以此类推
                   ->WordQuery.eval 

模板:OOB依赖于继承的多态性(指针多指),模板依赖于编译器对实参的实例化.
  模板定义:
 1,函数模板
  template <class T> inline int compare(const T&, const T&);  声明为内联函数模板的定义.后接返回类型.
  template <typename T,class T1, size_t T2>    定义
    模板形参表(template parameter list)包括类型形参(class/template T)和非类型形参(int T),逗号分开
  int compare( const T &v1, const T &V2 )
  {
   if ( v1 < v2 ) return -1;
   return 0;
  }
  compare(s1, s2);
 2,类模版
  template <class Type> class queue {   定义
  public:
   Queue ();
   Type &front ();
   const Type &front() const;
   void push(const Type &);
   void pop();
   bool empty() const;
  private:
   //...
  }
  queue< vector<double> > qvec;    使用
 类里包括数据成员,函数成员和类型成员. typename parm::size_type *p; 加typename来显式指定p的类型,
       parm::size_type *p;   而不是size_type数据成员.
 3,非类型的模板形参
  template <class T, size_t N> void array_init(T (&parm)[N])  包含N个T类型元素的数组的引用做形参
  {
   for ( size_t i = 0; i!= N; ++i ) {
    parm[i] = 0;
   }
  }
  int x[42]; array_init(x);
  实例化:实参类型的推断
 1,多个实参类型必须和形参相匹配.
 2,实参可以受限转换:   const自动忽视和转换;数组和函数自动装换为指针,int* *func
 3,非模板的实参还会做常规转换.  template <class Type> Type sum(const Type &op1, int op2)
 4,函数指针推断.
  template <typename T> int compare(const T&, const T&);
  int (*pf1)(const int&, const int&) = compare;   函数模板对函数指针的初始化和赋值
 5,不推断,显式实参:
  int i, short s; sum(static_cast<int>(s), i);  int sum(int, int)
  template <class T1, class T2, class T3> T1 sum(T2, T3);
  long val = sum<long>(i, lng)     指定返回为long型,T1,T2,T3对应于返回,第1实参,第2实参.
  template <typename T> int compare(const T&, const T&);
  void func(int(*) (const string&, const string&));
  void func(int(*) (const int&, const int&));
  func(compare<int>);      显式指明用compare int实例做初始化
  编译模型
 1,包含编译模型:在.h文件中包含声明和定义.
 2,分别编译模型;在.h文件中声明,在.c文件中定义.并用export template<class T> class queue扩展.
  类模板的定义
 1,完整的queue定义
 template <class T> class Queue;     类模板声明
 template <class T> std::ostream& operator<<(std::ostream&, const Queue<T>&); 
           std::ostream&表示形参是一个ostream型的引用,形参名在声明中省略.
 template <class T> class QueueItem {
  friend class Queue<T>;      特定同类型模板友
  friend std::ostream& operator<< <T>(std::ostream&, const Queue<T>&); 特定同类型普通友元
 // private class: no public section
  QueueItem(const T &t):item(t),next(0) { }    构造函数带参数和初始化,默认构造函数体
  T item;
  QueueItem *next; 
 }
 template <class T> class Queue {
  friend std::ostream& operator<< <T>(std::ostream&,const Queue<T>& );
 public:
  Queue(): head(0),tail(0) { }     默认构造 Queue是Queue<T>的缩写,是类模板的非限定名
  template <class It> Queue(It beg, It end):head(0), tail(0) { copy_elems(beg, end); } 模板成员
  Queue( const Queue &Q ): head(0),tail(0) { copy_elems(Q); } 复制构造带形参Q Queue<T>( const Queue<T> &Q):head(0) ...
  Queue& operator=( const Queue& );    赋值构造
  ~Queue() { destroy(); }      析构

  template <class Iter> void assign( Iter, Iter ); 模板成员声明
  T& front()  { return head->item; }     成员函数front声明
  const T &front() const { return head->item; }    重载函数front
  void push( const T & );      压入
  void pop();        弹出
  bool empty() const { return head==0; }    是否空
 private:
  QueueItem<T> *head;       数据成员
  QueueItem<T> *tail;
  void destory();       私有函数
  void copy_elems( const Queue& );
  template <class Iter> void copy_elems(Iter, Iter); 模板成员声明
 };
 template <class T> void Queue<T>::destory()    成员函数定义格式,模板形参表,作用域操作符,类后跟模板类型
 {
  while( !empty() )
   pop();
 }
 template <class T> void Queue<T>::pop() {
  QueueItem<T>* p = head;
  head = head->next;
  delete p;        删除p指定内存
 }
 template <class T> void Queue<T>::push(const T &val) {
  QueueItem<Type> *pt = new QueueItem<T>(val);   申请QueueItem大小内存.
  if ( empty() ) head = tail = pt;
  else{ tail->next = pt;
   tail = pt; }
 }
 template <class T> void Queue(T)::copy_perm(const Queue &orig){
  for ( QueueItem<T> *pt=orig.head, pt, pt=pt->next ) pt在编译时其值为orig.head
   push(pt->item);
 }
 用户调用类模版的成员函数,不会进行编译器实参推断,而是由类模板的实参决定. Queue<int> qi; qi.push(s); s编译时转化为int
 其实例化发生在为程序所用时,在定义的时候,实例化其默认构造函数,成员函数和模板类型.例如:顺序容器定义时,可以带没定义默认构造函数类型,但必须2个参数.
 2,
 template <int hi, int wid> class Screen {    非类型形参
 public:
  Screen():screen(hi*wid, '#'), cursor(0), height(hi), width(wid) { }
  ...
 private:
  std::string screen;
  std::string::size_type cursor;
  std::string::size_type height, width;
 };
 Screen<24, 80> hp2621;       用户定义

 类模板中的友元:
  1,普通友元
   template <class T> class Bar {
    friend class FooBar;    Foobar的成员和fcn函数可以访问Bar类的
    friend void fcn();    任意实例的private和protected成员,public都能访问
    ...
   }
  2,普通的模板友元
   template <class T> class Bar {
    template <class T1> friend class Foo1;  Fool的任意实例都可以访问Bar实例的私有元素
    template <class T2> friend void templ_fcn1(const T2&);
   }
  3,特定的模板友元
   template <class T1> class Foo2;
   template <nametype T2> void templ_fcn2(const T2&);
   template <class T> class Bar {
    friend class Foo2<char*>;
    friend void templ_fcn2<char*>(char* const *); 指定类型char*的实例化是Bar实例的友元
   }
   
   template <class T> class Bar {
    friend class Foo3<T>;
    friend void templ_fcn3<T> (const T&);  和Bar同类型实参的Foo3和templ_fcn3的实例是Bar的友元
   }
  4,声明的依赖性
   在设定模板友元之前或同时先进行友元类模板或函数模板的声明,没声明的是普通友元或编译出错.
   
  5,新增输出成员函数并定义友元:
  template <class T> ostream& operator<<(std::ostream &os, const Queue<T> &q)  书里没std::?? 
  {         Queue的输出操作符重载
   QueueItem<T> *p;
   for (p=q.head, p, p=p->next)
    os << p->item << " ";    3 5 8 13
   return os;     
  }
  template <class T> class QueueItem {
   friend class Queue<T>;         同类型友元Queue
   friend std::ostream& operator<< (T)(std::ostream&, const Queue<T>&);   同类型友元operator<<
  }
  template <class T> class Queue {
   friend std::ostream& operator<< (T)(std::ostream&, const Queue<T>&);   同类型友元operator<<
  }
  输出<<操作依赖于具体类型,类型本身要有<<操作,如没有,虽可以定义对象,但输出操作时会编译出错.  
 
 类模板中的模板成员:类类型(模板或非模板)可以包括类模板和函数模板成员,叫做成员模板(member remplate)
  成员模板使其他类型的数据可能导入类模板里,当然要先删除原来的类型.
  定义:
   template <class T> class Queue {
    public:
     template <class It> Queue(It beg, It end):head(0), tail(0) { copy_elems(beg, end); }
     template <class Iter> void assign(Iter, Iter);    声明
    private:
     template <class Iter> void copy_elems(Iter, Iter);   声明
   }   
  在类外部定义
   tempalte <class T> tempalte <class Iter> void Queue<T>::assign(Iter beg, Iter end)
   {
    destroy();
    copy_elems(beg, end);
   }
  成员模板的实例化
   templater <class T> template<class Iter> void Queue<T>::copy_elems(Iter beg, Iter end )
   {
    while ( beg != end ) {
     push(*beg);
     ++beg; }
   }
 static数据成员和成员函数
  template <class T> size_t Foo<T>::ctr = 0;   类外声明和初始化static数据成员
  tempalte <class T> class Foo{
  public:
   static std::size_t count() { return ctr;}  static 函数
  private:
   static std::size_t crt;     static 数据成员
  }
  Foo<int> f1,f2,f3;       实例化,f1,f2,f3共享一个static成员
  Foo<string> fs;       fs实例化另外一个str数据

  泛型句柄类:句柄能够动态申请和释放相关的继承类的对象.并将实际的工作转发到继承层次的底层类.
      泛型句柄类提供管理操作(计数和基础对象).
 定义
  template <class T> class Handle
  {
  public:
   Handle( T *p = 0 ):ptr(0),use(new size_t(1)) { }  构造函数1
   Handle( const Handle& h):ptr(h.ptr),use(h.use) {++*use;}; 构造函数2
   T& operation*();       *  解引用操作符
   T* operation->();       -> 成员访问操作符
   const T& operation*() const;     const *
   const T* operation->() const;     const ->
   Handle& operation=( const Handle& );    = 赋值操作符
   ~Handle() { rem_ref(); };      析构
  private:
   T *ptr;        继承对象指针
   size_t *use;        计数
   void rem_ref() { if(--*use == 0) {delete ptr; delete use;} } 无计数则释放空间
  }
  template <class T> inline Handle<T>& Handle<T>::operation=( const Handle &rhs )
  {
   ++*rhs.use;
   rem_ref();
   ptr = rhs.ptr;
   use = rhs.use;
   return *this;
  }
  template <class T> inline T& Handle<T>::operator*()
  {
   if (ptr) return *ptr;      判断一下是否为空
   throw std::runtime_error( "dereference of unbound Handle" );
  }
  template <class T> inline T* Handle<T>::operation->()
  {
   if (ptr) return ptr;
   throw std::runtime_error( "access through unbound Handle" );
  }
  template <class T> inline const ....

 使用
      1,Handle<int> hp( new int(42) );
  {
   handle<int> hp2 = hp;
   cout << *hp << " " << *hp2 << end1;    42 42
   *hp2 = 10;     
   cout << *hp << end1;       10
  }
      2,class Sales_item {
  public:
   Sales_item (): h() { }
   Sales_item( const Item_base &item ):h(item.clone()) { }
   const Item_base& operator*() const { return *h; }
   const Item_base* operator->() const { return h.operator->(); }
  private:
   Handle<Item_base> h;
  }
      3,double Basket::total() const
  {
   ...
   sum += (*iter)->net_price(items.count(*iter));   (*iter)返回h,h->是继承层次对象的指针.
  }
 
  模板特化
 1,函数模板特化:模板的一个或多个形参其实际类型或实际值是指定的.对于默认模板定义不适用的类型,特化非常有用.
  template<> int compare<const char*>( const char* const &v1, const char* const &v2 )
  {
   return strcmp( v1, v2 );
  }
 2,类模板特化
 3,特化成员而不特化类
 4,类模板的部分特化
  重载函数模板:匹配(带转换和隐式转换)原则,先普通函数再模板实例.
 template <typename T> int compare( const T&, const T& );
 template <class U, class V> int compare( U, U, V );
 int compare( const char*, const char* );
 compare( 1, 0 );       和int实例化匹配
 compare( ivec1.begin(), ivec1.end(), ivec2.begin() );  
 compare( ia1, ia1+10, ivec1.begin() );
 compare( const_arr1, const_arr2 );     选择普通函数优先模板版本
 compare( ch_arr1, ch_arr2 );     选择普通函数优先模板版本
 
 如果有重载模板函数定义为:
 template <typename T> int compare2(T, T);
 则不同,compare2( ch_arr1, ch_arr2 );    这时使用模板函数
  compare2( const_arr1, con_arr2 );    使用普通函数,还是定义函数模板的特化好些.

第五部分:进阶
异常处理:大型程序出错情况下的处理,程序的问题检测部分抛出一个对象,通过对象的类型和数据到处理部分处理.
  throw runtime_error("Data");创建一个异常对象(exception object,由编译器管理,保留在任意catch能访问的空间,处理完毕后释放),
   他是被抛出表达式的副本(局部存储在抛出块的空间,在处理异常时,一般抛出块不存在了),其结果必须是可复制的副本.
  catch(const runtime_error &e);可以匹配的第一个catch.

 抛出类类型的异常
  异常对象及继承:一般抛出的异常对象在静态编译时就决定了,其属于一个异常对象层次及其继承.
  异常指针:抛出的指针,其解引用的异常对象就是静态编译的类型,不会动态解引用到继承层次类.如果静态类型是基类,实参是派生类.
     则对象被分割,只抛出基类部分.单单抛出一个指针是错误的.
 栈展开(stack unwinding):在throw时,当前函数暂停,开始匹配catch语句,首先try块,再沿嵌套函数链继续向上.
       catch结束后,在紧接与try相关的最后一个catch之句之后的点继续执行.
  为局部对象自动调用析构函数:块的局部对象在栈展开时会调用析构函数自动释放,块直接分配单元new不会释放.
  析构函数从不抛出异常:析构函数不能throw异常,因为在stack unwinding时,在析构抛出未经过处理的异常,会导致调用标准库terminate函数,
     terminate调用abort函数,强制整个程序非正常退出.
  构造时抛出异常:构造时异常要保证部分已初始化的对象撤销.
  未捕获的异常终止程序:没匹配到得异常,编译器自动调用库函数terminate.
 捕获异常catch(exception specifier)
  匹配原则:比匹配实参与形参类型的规则严格.除以下区别,异常对象类型必须与catch说明符类型完全匹配.不能算数转换,不能类类型转换.
   1,允许非const到const的转换.
   2,允许派生类到基类的转换.
   3,将数组转换为指向数组类型的指针,将函数转换为函数指针.
  异常说明符:可以是异常对象的引用,或者是异常对象的副本.
  异常说明符与继承:派生类的异常对象可以通过引用和指针和catch形参匹配,如果异常派生对象传递到基类说明符类型,则副本是分割的基类子对象.
      catch子句的次序必须和派生层次对应,派生类的catch要在基类的catch之前.
 重新抛出:在校正了一些行动后,catch一般rethrow原来的传进来的异常对象,异常对象的引用的改变可以rethrow, throw;
  1,捕获的所有异常的处理代码: throw(...) { } 捕获任何类型的异常,在其他catch之后.
  2,函数测试块与构造函数: 在构造函数体之前的初始化时,也可能发生异常,这时使用函数测试块(func try block).
     template <class T> Handle<T>::Handle(T *p)
     try : ptr(p), use(new size_t(1))     func try block
     {
      //empty function body
     } catch( const std::bad_alloc &e )
      { handle_put_of_memory(e); }
  3,异常类层次: exception -> bad_cast
         -> runtime_error -> overflow_error     运行错误
                -> underflow_error
                -> range_error
         -> logic_error   -> domain_error      逻辑错误
                -> invalid_argment
                -> out_of_range
                -> length_error
         -> bad_alloc
   异常类的派生:
    class isbn_mismatch : public std::logic_error {
    public:
     explicit isbn_mismatch( const std::string &s ) : std::logic_error(s) { }
     isbn_mismatch( const std::string &s, const std::string &lhs, const std::string &rhs ) :
         std::logic_error(s), left(lhs), right(rhs) { }
     const std::string left, right;    共有的数据成员
     virtul ~isbn_mismatch() throw() { }
    };
   派生类的使用:
    Sales_item operator+( const Sales_item& lhs, const Sales_item& rhs )
    {
     if ( !lhs.same_isbn(rhs) )
      throw isbm_mismatch( "isbn mismathc", lhs.book(), rhs.book() );
     Sales_item ret(lhs);
     ret += rhs;
     return ret;
    }
    Sales_item item1, item2, sum;
    While ( cin >> item1 >> item2 ) {
     try {
      sum = item1 + item2;
     }
     catch ( const isbn_mismatch &e ) { cerr << e.what() << e.left << e.right << endl; }
    } 
  4,自动资源释放: 局部对象会自动撤销,但通过new申请的数组不会自动撤销.
      通过异常安全技术(exception safe),可以定义一个类来封装资源的分配和释放.既"资源分配既初始化"RAII.
    class Resource {
    public:
     Resource(parms p) : r(allocate(p)) { }
     ~Resource() { release(r); }
    private:
     resource_type *r;     私有数据
     resource_type *allocate(parms p);  私有成员函数allocate
     void release(resource_type*);   私有成员函数resource_type
    };
    void fcn()
    {
     Resource res(args);
     ...
    }
 auto_ptr类: 是个RAII的模板,#include <memory>
  auto_ptr<T> ap; auto_ptr<T> ap(p);ap拥有指针p指向的对象,explicit关闭隐形转换的. auto_ptr<T> ap(ap2);
  apq = ap2; ~ap; *ap; ap->成员访问操作符 ap.reset(p); ap.release(); ap.get();
  auto_ptr类型只能管理new返回的一个对象,不能管理动态分配的数组.不能将auto_ptr作为标准库容器的类型,
           (其在复制和赋值时有不同于普通指针的行为).
  auto_ptr类只能保存指向一个对象的指针,不能指向动态分配的数组.但其撤销时,就自动回收auto_ptr指向的动态分配对象.
  1,为new申请的内存使用auto_ptr.
  2,auto_ptr可以保存任意类型的指针, 
  3,auto_ptr<string> apl(new string("Brontosaurus"));
  4,*ap1 = "TRex"; sting s = *ap1; if (ap1->empty())
  5,复制和赋值后原来的右auto_ptr对象指向空.
  6,左操作数指向的对象会删除.
  7,auto_otr的默认构造函数初始化为0.
  8,测试auto_ptr对象,if( p_auto.get() )
  9,p_auto = new int(1024); //error不能直接传指针
    if ( p_auot.get() ) *p_auto = 1024;
    else p_auto.reset(new int(1024));
  警告:1,不能用auot_ptr指向const数据 2,2个auto_ptr不能指向同一个对象3,不能指向数组4,不能存于容器.
 异常说明:
  定义: void recoup(int) throw(runtime_error) 异常类型列表
   void recoup(int) throw();   不抛出任何异常
  违反异常说明: 抛出没在异常类型表的异常,编译器没提示,会自动调用unexpected,unexpected调用terminate终止程序.
  确定函数不抛出异常: throw();
  成员函数异常说明: virual const char* what() const throw();
  析构函数异常说明: vitual ~isbn_mismatch() thrwo() { } 析构函数不能抛出异常  
  异常说明与虚函数: 基类虚函数的抛出异常类型表必须大于派生类虚函数的异常类型表.这样基类指针访问派生类时不会unexpected.
 函数指针的异常说明:
  void recoup(int) throw(runtime_error);
  void (*pf1) (int) throw(runtime_error) = recoup;    匹配,ok
  void (*pf2) (int) throw(tuntime_error, logic_error) = recoup;   超出,error
  void (*pf3) (int) throw() = recoup;      不抛出,ok
  void (*pf4) (int) = recoup;        没异常说明,ok    
命名空间:解决第三方库的多重名问题(命名空间污染 namespace pollution),库一般会在全局里定义类,函数,类别.
 定义: 在全局或其他namespace定义,不能在类和函数里定义.
   namespace cplus_primer{
    class Sales_item { /* ... */ };   可以包括类,函数,变量及其初始化,模板,其他namespace
    Sales_item operator+( const Sales_item&, const Sales_item&);
    class Query {
    public:
     Query( const std::string& );
     std::ostream &display( std::ostream& ) const;
     //... 
    };
    class Query_base { /* ... */};
   }
   cplusplus_primer::Query q = cplusplus_primer::Query("hello");  在其它域引用
   q.display(cout);
   using cplusplus_primer::Query;       声明
   namespace namespace_name { }       定义和添加定义
  namespace可以在.h里声明,其成员定义在.c里( namespace cplusplus_primer{ } ).保证接口和实现的分离.
  定义可以在命名空间里,在全局里,但是不能在其它namespace里.
 嵌套命名空间: 嵌套里同名的声明屏蔽其外围命名空间中的声明. 调用cplusplus_primer::QueryLib::Query 
 未命名的命名空间: namespace { 声明块 }; 适用于本文件,可以在同文件里不连续,其成员名不能和全局重名.
    如果是.h里的未命名空间,则在不同的.c里定义局部实体.
    c++不赞成c中的静态声明,即static func();改用namespace代替.
 命名空间成员的使用: using std::map; 一次定义一个,一般定义在函数体或其他作用域的内部.在作用域结束时结束.
    namespce Qlib = cplusplus_primer::QueryLib; 命名空间别名.
    namespace blip{ int bi = 16, bj = 15, bk = 23; }
    void main()
    {
     using namspace blip;  using指示,在函数里定义,表明namespace成员局部可见.
     ++bi; --bj; ...
    }
 类,命名空间和作用域: 命名空间是作用域.其成员从声明时可见,透过嵌套和类,直至块的末尾.
    std::string s; getline( std::cin, s ); 编译器会关联形参作用域到函数名.
    接受类类型实参的友元和类在同一个命名空间,引用时无须使用显式命名空间限定符.
    void f2() { A::C cobj; f(cobj); }
 重载与命名空间: 确定候选函数集合,再匹配,再二义性.
    using NS::print 引入所有的重载到候选集合,单引入某个重载函数会导致程序奇怪的行为.
    using namespace 也一样.
 命名空间与模板: 主要是特化的命名空间,模板特化的声明,定义和引用和其它命名空间成员一样.

复杂的继承关系:多重继承(multiple inheritance),虚继承
 多重继承:多余一个基类的派生类的能力,用于建模.
   class Panda : public Bear, public Endangered { };
   ZoomAnimal -> Bear   -> panda
     Endangered ->
   panda::panda(std::string name, bool onExhibit) :   派生类构造函数初始化所有基类.
    Bear(name, onExhibit, "Panda"),     如省略,则调用默认构造函数.
    Endangered(Endangered::critical) {  }
   构造的次序按基类构造函数在类派生列表中出现的次序调用,与构造函数中基类出现的次序无关.
    ZoomAnimal -> Bear -> Endangered -> Panda
   析构的次序则反过来.~Panda,~Endangered,~Bear,~ZooAnimal. 

  多个基类的转换:对派生类的指针或引用可以转换为任何基类的指针和引用,二义性的报错.
    和单继承一样,对基类的指针和引用只能访问基类的(或继承的)成员.不能访问到派生类.
    无论调用继承层次中的那个虚析构函数,处理都是按构造的逆序,全部析构. 

  多重继承派生类的复制控制:合成的复制和赋值是全体的,按次序由低到高.ZoomAnimal,Bear,Endangered,Panda.
         派生类自己定义的则只负责基类子部分.

  多重继承下的类作用域:成员函数中使用的名字首先在函数体内部查找,再在类里查找,再按继承层次由上往下查找.
     相同的要指定名字,否则具有二义性.
    多基类导致的二义性,即使是多声明,形参不同也会.使用 ying_yang.Endangered::print(cout) 可避免二义性.
    为避免二义性,可以定义一个新的函数.
    std::ostream& panda::print(std::ostream &os) const {  类里数据只是传值而不发生变化.
     Bear::print(os); 
     Endangered::print(os);
     return os;
    }

 虚继承:在定义继承层次时,ios基类管理流的状态和读写缓冲区,istream/ostream实现流操作,iostream多重继承istream/ostream.按常规继承其有2个ios.
  这时需要共享虚类子对象ios,称为虚基类(virtual base class).
  声明:  class istream : public virtual ios { ... };  指定虚继承ios派生istream
    class ostream : virtual public ios { ... };
  虚继承不是直观的,必须在提出虚继承之前虚派生.虚继承一般不会引起问题,他通常由一个人开发.
   Zooanimal  (虚)-> Bear    ->
        (虚)-> Raccon  ->  Panda(前一级虚)
           Endangered  -> 
  定义完的虚基类支持派生类对象到虚基类的转换.
  虚继承的特殊的初始化语义:
   Panda::Pamda( dtd::string name, bool onExhibit ) :
    ZooAnimal(name, onExhibit, "Panda"),   直接初始化虚基类ZooAnimal,第1构造
    Bear(name, Onexhibit),     第2构造,忽略Bear的ZooAnimal构造函数初始化列表.
    Raccoon(name, onExhibit),     第3,忽略Raccoon的ZooAnimal构造函数初始化列表.
    Endangered(Endangered::critical),    第4
    sleeping_flag(false) {  }     第5
            最后,构造Panda本身
   带虚基类的继承层次,首先按先后构造虚基类(不管显式的还是合成的初始化),再按次序正常构造其他类.析构则相反.
       
优化内存分配: 一般是分配内存,并构造初始化.(new),但效率不高,
  且vector不能预知类型;这时一般先申请一段内存,使用时在构造.优化一般实现后再做.
  对未构造的对象进行操作而不是初始化,其结果是运行崩溃.
 allocator类: 模板,提供可知类型的内存分配操作,但不初始化. 其成员construct和destory分别在未构造内存中初始化对象和析构.
   allocate<T> a;   a.allocate(n);   a.deallocate(p, n);
   a.construct(p, t);   a.destroy(p);   
   uninitialized_copy(b, e, b2); uninitialized_fill(b, e, t); uninitialized_fill_n(b, e, t, n);未初始化构造
  template <class T> class vector {
  public:
   Vector() : elements(0), first_free(0), end(0) {  }
   void push_back(const T&);
   // ...
  private: 
   static std::allocator<T> alloc;  定义alloc
   void reallocate();    声明成员函数reallocate
   T* elements;     指向数组的第一元素
   T* first_free;    指向第一个空的元素
   T* end;     指向数组本身之后的元素
   // ...
  }
  template<class T> vector<T>::push_back( const T& t )
  {
   if ( first_free == end ) reallocate(); 申请一段新的空间,并进行复制,first_free指向新空间.
   alloc.construct( first_free, t );  
   ++first_free;
  }
  template <class T> vector<T>::reallocator()
  {
   std::ptrdiff_t size = first_free - elements;
   std::ptrdiff_t newcapcity = 2 * max(size, 1);
   T* newelements = alloc.allocate(newcapacity);   申请新空间
   uninitialized_copy( elements, first_free, newelements ); 未初始化复制  
   for (T *p = first_free; p != elements; /* empty */) alloc.destory( --p ); 
   if ( elements ) alloc.deallocate( elements, end - elements );逆序释放原空间
   elements = newelements;      重定位置
   first_free = elements + size;
   end = elements + newcapacity;
  }

 operator new/operator delete:标准库机制,new/delete调用,会分配或释放可知大小,未类型化(既未初始化)的内存,
      uninitialized_fill/uninitialized_copy构造对象,析构函数析构对象.
  void *operator new(size_t);  void *operator new[](size_t);
  void *operator delete(void*); void *operator delete[](void*);
  T* newelements = alloc.allocate( newcapacity );  和operator new功能一样,参数带类型,安全
  T* newelements = static_cast<T*>( operator new[](newcapacity*sizeof(T)) ); 静态类型转换.

 定位new表达式: 接受未构造的内存的指针,并在该空间初始化一个对象或一个数组.
  new (place_address) type(initializer-list)
  new (first_free) T(t);

  allocator<string> alloc;
  string *sp = alloc.allocate(2);   构造临时对象
  new (sp) string(b, e);    一对迭代器做实参
  alloc.construct(sp+1, string(b, e));  构造后的复制,有些类的复制构造函数是私有的,不能复制,必须用定位new表达式.  

 显式析构函数的调用: 要清除对象时
  1, alloc.destroy( p );
  2, p->~T;   直接析构成员对象,但内存还在,可以再用.

 类自定义的new和delete:
  类定义或继承了自定义的operator new/operator delete,就忽略标准库的new/delete.或operator new[]/operator delete[].
  void* operator new(size_t) void operator delete(void*, size_t) 只访问静态成员,默认是静态的,不用static
  void* operator new[](size_t) void oeprator delete[](void*, size_t)
  在类自定义了operator new/delete后,可以使用全局作用域操作符::强制使用标准库的new/delete
  Type *p = ::new Type; ::delete p;

 CacheObj内存分配器基类:分配和管理已分配但未构造的自由列表(相当于在一段内存上添加和释放成员对象).希望自定义的new/delete的类可以继承CacheObj.
 1,定义 template <class T> class CacheObj {
  public:
   void *operator new(std::size_t);   自定义new
   void operator delete( void*, std::size_t ); 自定义delete
   virtual ~CacheObj();    虚析构函数,因为是虚基类.
  protected:
   T *next;
  private:
   static void add_to_freelist(T*);
   static std::allocator<T> alloc_mem;
   static T *freeStore;
   static const std::size_t chunk;
  };
 2,成员 template <class T> void *CachedObj<T>::operator new(size_t sz)
  {
   if ( sz != sizeof(T) )
    throw srd::runtime_error( "CachedObj: wrong size object in operator new" );
   if ( !freestore ) {
    T* array = alloc_mem.allocate(chunk);   申请chunk个T的空间
    for ( size_t i=0; i!=chunk; ++i )
     add_to_freelist(&array[i]);    用基类的next将其连起
   }
   T *p = freeStore;
   freeStore = freeStore->CachedObj<T>::next;   下一个剩余空间
   return p;        返回现在的地址指针
  }
 3, template <class T> void CachedObj<T>::operator delete(void *p, size_t)
  {
   if ( p != 0 )  add_to_freelist(static_cast<T*>(p));  将现在的指针指向的空间放回.
  }
 4, template <class T> void add_for_freelist(T *p)
  {
   p->CachedObj<T>::next = freeStore;
   freeStore = p;
  }
 5, template <class T> alloctor<T> CachedObj<T>::alloc_mem;  定义静态成员
  template <class T> T *CachedObj<T>::freeStore = 0;
  template <class T> const size_t CachedObj<T>::chunk = 24;

 6,应用 class Screen : public CacheObj<Screen> { };      类继承模板CacheObj
  template <class T> class QueueItem : public CacheObj< QueueItem<T> > { }; 模板继承模板CacheObj

运行时的类类型识别RTTI: 通过基类的指针和引用来检索其运行时对象的实际派生类型.
   带虚函数的类,在运行时执行RTTI操作符,其它的在编译时计算RTTI操作符.程序员必须知道对象强制转换为哪种类型.
   要想通过基类来操作派生类,最好的办法还是虚函数,使用虚函数时,编译器会自动根据对象的实际类型选择正确的函数.
 dynamic_cast操作符: 将基类类型的指针和引用安全转换为派生类型的指针和引用.
  if ( Derived *derivedPtr = dynamic_cast<Derived*>(basePtr) )  强制基类指针转换使用if/else
  {
   //use Derived object
  }else{
   //use Base object
  }
  void f(const Base &b)        强制基类引用转换
  {
   try {
    const Derived &d = dynamic_cast<const Derived&>(b);
   }catch ( bad_cast ) {   
   }
  }
 typeid操作符:  返回指针或引用所指对象的实际类型.返回type_info
  if ( typeid(*bp) == typeid(Derived) ) {      必须是*bp,bp所指的对象.
  }         如果bp是带虚函数的类型,typeid(*bp)抛出bad_typeid异常.
  
 RTTI的使用:
  举例:在比较继承层次的对象equal时,先比较运行类型相等,再逐层调用虚函数比较.
  class Base {
   friend bool operator==(const Base&, const Base&);
  public:
  protected:
   virtual bool equal(const Base&) const;
  };
  class Derived : public base {
   friend bool operator==(const Base&, const Base&);
  public:
  private:
   bool equal(const Base&) const;
  }
  bool operator==(const Base &lhs, const Base &rhs)
  {
   return typeid(lhs) == typeid(rhs) && lhs.equal(rhs);
  }
  bool Derived::equal( const Base &rhs ) const
  {
   if ( const derived *dp = dynamic_cast<const Derived*>(&rhs) ) {  必须强制转换
   }else return false;
  }

 type_info类:提供操作
  t1 == t2; t1 != t2; t.name(); t1.before(t2);

 类成员指针: 对象指针通过->成员访问符访问成员,也可以先直接定义类成员的指针,再使用这个指针绑定到函数或数据成员.
   static成员不是对象的组成部分,不能定义类成员的指针.
  class Screen {
  public:
   typedef std::string::size_type index;
   char get() const;
   char get( index ht, index wd ) const;
  private:
   std::string contents;
   index cursor;
   index height, width;
  };
  数据成员的指针:
  string Screen::*ps_screen = &Screen::contents;   Screen里的std::string类型成员contents的指针.
  Screen::index Screen::*pindex = &Screen::width;  指针指向screen里的index类型成员,cursor,height,width.
  函数成员的指针:
  char (Screen::*)() const   这个类型指定Screen类的const成员函数指针,无形参,返回char类型的值.
  char (Screen::*pmf2)() const = &Screen::get;   定义类成员函数指针
  char (Screen::*pmf2)(Screen::index, Screen::index) const = &Screen::get;
  char Screen::*p() const; //error:mon-member function p connot have const qualifier
      调用操作符的优先级高于成员指针操作符,所以括号是必须的.
  成员指针的类型别名:
  typedef char (Screen::*Action)(Screen::index, Screen::index) const; 
       Action是类型"Screen类的接受两个index类型,返回char的成员函数的指针"的名字.
  Action get = &Screen::get;  定义get,Action类型.复制为&Screen:get

  Screen& action(Screen&, Action = &Screen::get);  声明函数action带形参Screen&和Action,返回Screen&
  Screen myscreen;
  action(myscreen);      use the default argument
  action(myscreen, get);     use the variable get taht we priviously defined
  action(myscreen, &Screen::get);    pass address explicitly(明确的)

 使用类成员指针:
  成员指针解引用操作符(.*) :从对象或引用获取成员.
  成员指针箭头操作符(->*) :通过对象的指针获取成员.
  1,使用成员函数的指针.
   char c2 = (myscreen.*pmf)();  char c2 = (pScreen->*pmf)(0, 0);
  2,使用数据成员的指针.
   Screen::index ind2 = myScreen.*pindex; Screen::index ind2 = pScreen->*pindex;
  3,应用
  class Screen {
  public:
   Screen& home();     成员函数指针表.
   Screen& forward();
   Screen& back();
   Screen& up();
   Screen& down();

   typedef screen& (Scren::*Action)();  定义成员函数指针类型 
   static Action Menu[];    定义指针数组

   enum Directions { HOME, FORWARD, BACK, UP, DOWN }; 枚举
   Screen& move(Directions);    操作成员函数move 
  };
  Screen::Action Screen::Menu[] = { &Screen::home, 定义成员函数指针表.
       &Screen::forward,
       &Screen::back,
       &Screen::up,
       &Screen::down,
       };
  Screen& Screen::move(Directions cm)
  {
   (this->*Menu[cm])();     使用函数指针表.
   return *this;
  };
  Screen myScreen;
  myScreen.move(Screen::HOME);
  myScreen.move(Screen::DOWN);

嵌套类:nested class在一个类里定义一个新类,这个新类叫嵌套类.一般用于定义执行类,2个类的对象的成员互相独立,互相独立.
 嵌套类的名字只在外围类的作用域可见,在其它类作用域或定义外围类的作用域不可见.
 template<class T> class Queue{     模板Queue
 private:
  struct QueueItem;      私有嵌套类声明
  QueueItem *head;
  QueeuItem *tail;
 };
 tempalte <class T> struct Queue<T>::QueueItem {  在外围类的外部定义嵌套类,也是一个模板(T)
  QueueItem(const T &t) : item(t),next(0) { }
  T item;
  QueueItem *next;
 };
 template <class T> int Queue<T>::QueueItem::static_mem = 1024;嵌套类静态成员定义
    嵌套类可以直接引用外围类的静态成员,类型名和枚举成员.引用外围作用域以外的要加作用域确定操作符. 
 template <class T> void Queue<T>::pop() {    外围类使用嵌套类.
  QueueItem *p = head;
  head = p->next;
  delete p;
 }

 实例化: Queue<int> qi; 实例化Queue,但不实例化QueueItem.当head/tail解引用时才实例化QueueItem.

 嵌套类作用域中的名字查找???
 
联合类:节省空间的类,具有某些类特征:可以包括构造,析构,赋值.不能有static或引用成员.
 class Token {
  enum Tokenkind { INT, CHAR, DBL };
  TokenKind tok;
  union {
   char cval;
   int ival;
   double dval;
  } val;
 };
 switch ( token.tok ) {
 case Token::INT:
  token.val.ival = 42;  break;    //.val.
 case Token::CHAR:
  token.cval = 'a';  break;    //匿名联合可以直接访问其成员
 };

局部类:在函数体内可以定义局部类,再进一步封装数据.局部类不能声明static数据,可以访问外围作用域的static变量,枚举,类型名.
 int a, val;        全局变量
 void foo( int val )       函数体定义
 {
  static int si;      外围作用域变量
  enum Loc { a = 1024, b };     外围作用域enum
  class Bar {       
  public:       一般都是public,不需再封装
   Loc locVal;      ok,use local type name
   int barVal;
   void fooBar( Loc l = a )    ok
   {
    barVal = val;     err,val is local to foo
    barVal = ::val;    ok
    barVal = si;     ok,use static
    locVal = b;     ok
   }
  };
 }
 类成员声明所用名字必须出现在之前的作用域中,
 成员定义中所用名字可以出现在局部类作用域任何地方,
 没找到在外围作用域找,再到包含函数的作用域找.

不可移植的一些特征:支持低级编程,从gcc语言继承来的.
 位域:  typedef unsigned int Bit;  位域必须是int型
   class File {
    Bit mode: 2;   Bit是位域,2位.
    Bit modified: 1;
    Bit prot_owner: 3;
    Bit prot_group: 3;
    Bit prot_world: 3;
    ...
   };
   void File::write()   成员函数可以直接使用位域.
   {
    modified = 1;
   }
   enum { READ = 01, WRITE = 02 };
   int main() {
    File myFile;
    myFile.mode |= READ;
    if ( myFile.mode & READ )
     cout << " myFile.mode READ is set/n";
   }
  &不能应用于位域,位域也不能是类的静态成员(多对象共享,省内存).

 volatile限定符:一些数据的值由程序之外的硬件所控制, 如时钟.其对象声明为volatile:
      (不优化,,编译器就不会把那个变量缓存到寄存器中,对变量的每次访问都直接通过实际内存的位置).
  volatile int display_register,v;
  int *volatile vip;   vip is a volitile pointer to int
  volatile int *ivp;   ivp is a pointer to valatile int
  ivp = &v;     只能用volitile的引用初始化
  类合成的复制控制不适用于volatile对象.必须重定义带volatile成员的类的复制控制.
  
 extern "c":链接指示(linkage directive),不能出现在类定义或函数定义内部,它必须出现在函数的第一次声明上.
  1,extern "C" size_t strlen(const char *);
  2,extern "C" {
   int strcmp( const char *, const char* );
   char *strcat( char*, const char* );
   }
  3,extern "C" { #incldue <string.h> }   string.h必须是c或c++函数.
  4,extern "C" double calc(double dparm) { /* ... */} 链接指示也可以将c++导出
  5,extern "Ada"   extern "FORTRAN"
  6,#ifdef __cplusplus     如果编译c++,就extern "c"
    extern "C"
    #endif
    int strcmp( const char*, const char* );
  7,c++的重载函数只能链接指示一个到c.
   extern "C" double calc(double);   c/c++调用
   extern SmallInt calc(const SmallInt&);  c++调用
   extern BigNum calc(const BigNum&);   c++调用
  8,void (*pf1)(int);      c++
    extern "C" void (*pf2)(int);    c
    pf1 = pf2;       //error;c和c++指针具有不同类型.
  9,extern "C" void f1(void(*)(int));   应用于整个声明的链接指示
    fi是个c函数,返回void,形参void(*)(int):c函数指针.
   extern "C" typedef void FC(int);   类型别名
   void f1(FC *);     访问

 

 

 

 

  
 

 

【原书名】 C++ Primer (4th Edition) 【原出版社】 Addison Wesley/Pearson 【作者】 (美)Stanley B.Lippman,Josée LaJoie,Barbara E.Moo 【译者】 李师贤 蒋爱军 梅晓勇 林瑛 【丛书名】 图灵计算机科学丛书 【出版社】 人民邮电出版社 【书号】 7-115-14554-7 【开本】 16开 【页码】 900 【出版日期】 2006-3-1 【版次】 4-1 【内容简介】 本书是久负盛名的C++经典教程,其内容是C++大师Stanley B. Lippman丰富的实践经验和C++标准委员会原负责人Josée Lajoie对C++标准深入理解的完美结合,已经帮助全球无数程序员学会了C++。本版对前一版进行了彻底的修订,内容经过了重新组织,更加入了C++ 先驱Barbara E. Moo在C++教学方面的真知灼见。既显著改善了可读性,又充分体现了C++语言的最新进展和当前的业界最佳实践。书中不但新增大量教学辅助内容,用于强调重要的知识点,提醒常见的错误,推荐优秀的编程实践,给出使用提示,还包含大量来自实战的示例和习题。对C++基本概念和技术全面而且权威的阐述,对现代C++编程风格的强调,使本书成为C++初学者的最佳指南;对于中高级程序员,本书也是不可或缺的参考书。本书的前言阐述了 第4版和前一版的不同之处。 【目录信息】 第1章 快速入门 1 1.1 编写简单的C++程序 2 1.2 初窥输入/输出 5 1.2.1 标准输入与输出对象 5 1.2.2 一个使用IO库的程序 5 1.3 关于注释 8 1.4 控制结构 10 1.4.1 while语句 10 1.4.2 for语句 12 1.4.3 if语句 14 1.4.4 读入未知数目的输入 15 1.5 类的简介 17 1.5.1 Sales_item类 17 1.5.2 初窥成员函数 19 1.6 C++程序 21 小结 22 术语 22 第一部分 基本语言 第2章 变量和基本类型 29 2.1 基本内置类型 30 2.1.1 整型 30 2.1.2 浮点型 32 2.2 字面值常量 34 2.3 变量 38 2.3.1 什么是变量 39 2.3.2 变量名 40 2.3.3 定义对象 42 2.3.4 变量初始化规则 44 2.3.5 声明和定义 45 2.3.6 名字的作用域 46 2.3.7 在变量使用处定义变量 48 2.4 const限定符 49 2.5 引用 50 2.6 typedef名字 53 2.7 枚举 53 2.8 类类型 54 2.9 编写自己的头文件 57 2.9.1 设计自己的头文件 58 2.9.2 预处理器的简单介绍 60 小结 62 术语 62 第3章 标准库类型 67 3.1 命名空间的using声明 68 3.2 标准库string类型 70 3.2.1 string对象的定义和初始化 70 3.2.2 String对象的读写 71 3.2.3 string对象的操作 72 3.2.4 string对象中字符的处理 76 3.3 标准库vector类型 78 3.3.1 vector对象的定义和初始化 79 3.3.2 vector对象的操作 81 3.4 迭代器简介 83 3.5 标准库bitset类型 88 3.5.1 bitset对象的定义和初始化 88 3.5.2 bitset对象上的操作 90 小结 92 术语 92 第4章 数组和指针 95 4.1 数组 96 4.1.1 数组的定义和初始化 96 4.1.2 数组操作 99 4.2 指针的引入 100 4.2.1 什么是指针 100 4.2.2 指针的定义和初始化 101 4.2.3 指针操作 104 4.2.4 使用指针访问数组元素 106 4.2.5 指针和const限定符 110 4.3 C风格字符串 113 4.3.1 创建动态数组 117 4.3.2 新旧代码的兼容 120 4.4 多维数组 122 小结 124 术语 125 第5章 表达式 127 5.1 算术操作符 129 5.2 关系操作符和逻辑操作符 131 5.3 位操作符 134 5.3.1 bitset对象或整型值的使用 135 5.3.2 将移位操作符用于IO 137 5.4 赋值操作符 137 5.4.1 赋值操作的右结合性 138 5.4.2 赋值操作具有低优先级 138 5.4.3 复合赋值操作符 139 5.5 自增和自减操作符 140 5.6 箭头操作符 142 5.7 条件操作符 143 5.8 sizeof操作符 144 5.9 逗号操作符 145 5.10 复合表达式的求值 145 5.10.1 优先级 145 5.10.2 结合性 146 5.10.3 求值顺序 148 5.11 new和delete表达式 150 5.12 类型转换 154 5.12.1 何时发生隐式类型转换 154 5.12.2 算术转换 155 5.12.3 其他隐式转换 156 5.12.4 显式转换 158 5.12.5 何时需要强制类型转换 158 5.12.6 命名的强制类型转换 158 5.12.7 旧式强制类型转换 160 小结 161 术语 162 第6章 语句 165 6.1 简单语句 166 6.2 声明语句 167 6.3 复合语句(块) 167 6.4 语句作用域 168 6.5 if语句 169 6.6 switch语句 172 6.6.1 使用switch 173 6.6.2 switch中的控制流 173 6.6.3 default标号 175 6.6.4 switch表达式与case标号 176 6.6.5 switch内部的变量定义 176 6.7 while语句 177 6.8 for循环语句 179 6.8.1 省略for语句头的某些部分 180 6.8.2 for语句头中的多个定义 181 6.9 do while语句 182 6.10 break语句 183 6.11 continue语句 184 6.12 goto语句 185 6.13 try块和异常处理 186 6.13.1 throw表达式 186 6.13.2 try块 187 6.13.3 标准异常 189 6.14 使用预处理器进行调试 190 小结 192 术语 192 第7章 函数 195 7.1 函数的定义 196 7.1.1 函数返回类型 197 7.1.2 函数形参表 198 7.2 参数传递 199 7.2.1 非引用形参 199 7.2.2 引用形参 201 7.2.3 vector和其他容器类型的形参 206 7.2.4 数组形参 206 7.2.5 传递给函数的数组的处理 209 7.2.6 main:处理命令行选项 210 7.2.7 含有可变形参的函数 211 7.3 return语句 211 7.3.1 没有返回值的函数 212 7.3.2 具有返回值的函数 212 7.3.3 递归 216 7.4 函数声明 217 7.5 局部对象 220 7.5.1 自动对象 220 7.5.2 静态局部对象 220 7.6 内联函数 221 7.7 类的成员函数 222 7.7.1 定义成员函数的函数体 223 7.7.2 在类外定义成员函数 225 7.7.3 编写Sales_item类的构造 函数 225 7.7.4 类代码文件的组织 227 7.8 重载函数 228 7.8.1 重载与作用域 230 7.8.2 函数匹配与实参转换 231 7.8.3 重载确定的三个步骤 232 7.8.4 实参类型转换 234 7.9 指向函数的指针 237 小结 239 术语 240 第8章 标准IO库 243 8.1 面向对象的标准库 244 8.2 条件状态 247 8.3 输出缓冲区的管理 249 8.4 文件的输入和输出 251 8.4.1 文件流对象的使用 251 8.4.2 文件模式 254 8.4.3 一个打开并检查输入文件的 程序 256 8.5 字符串流 257 小结 259 术语 259 第二部分 容器和算法 第9章 顺序容器 263 9.1 顺序容器的定义 264 9.1.1 容器元素的初始化 265 9.1.2 容器内元素的类型约束 267 9.2 迭代器和迭代器范围 268 9.2.1 迭代器范围 270 9.2.2 使迭代器失效的容器操作 271 9.3 顺序容器的操作 272 9.3.1 容器定义的类型别名 272 9.3.2 begin和end成员 273 9.3.3 在顺序容器中添加元素 273 9.3.4 关系操作符 277 9.3.5 容器大小的操作 278 9.3.6 访问元素 279 9.3.7 删除元素 280 9.3.8 赋值与swap 282 9.4 vector容器的自增长 284 9.5 容器的选用 287 9.6 再谈string类型 289 9.6.1 构造string对象的其他方法 290 9.6.2 修改string对象的其他方法 292 9.6.3 只适用于string类型的操作 293 9.6.4 string类型的查找操作 295 9.6.5 string对象的比较 298 9.7 容器适配器 300 9.7.1 栈适配器 301 9.7.2 队列和优先级队列 302 小结 303 术语 303 第10章 关联容器 305 10.1 引言:pair类型 306 10.2 关联容器 308 10.3 map类型 309 10.3.1 map对象的定义 309 10.3.2 map定义的类型 310 10.3.3 给map添加元素 311 10.3.4 使用下标访问map对象 311 10.3.5 map::insert的使用 313 10.3.6 查找并读取map中的元素 315 10.3.7 从map对象中删除元素 316 10.3.8 map对象的迭代遍历 316 10.3.9 “单词转换”map对象 317 10.4 set类型 319 10.4.1 set容器的定义和使用 319 10.4.2 创建“单词排除”集 321 10.5 multimap和multiset类型 322 10.5.1 元素的添加和删除 322 10.5.2 在multimap和multiset 中查找元素 323 10.6 容器的综合应用:文本查询程序 325 10.6.1 查询程序的设计 326 10.6.2 TextQuery类 327 10.6.3 TextQuery类的使用 328 10.6.4 编写成员函数 330 小结 332 术语 332 第11章 泛型算法 335 11.1 概述 336 11.2 初窥算法 339 11.2.1 只读算法 339 11.2.2 写容器元素的算法 341 11.2.3 对容器元素重新排序的算法 343 11.3 再谈迭代器 347 11.3.1 插入迭代器 348 11.3.2 iostream迭代器 349 11.3.3 反向迭代器 353 11.3.4 const迭代器 355 11.3.5 五种迭代器 356 11.4 泛型算法的结构 358 11.4.1 算法的形参模式 359 11.4.2 算法的命名规范 359 11.5 容器特有的算法 361 小结 362 术语 363 第三部分 类和数据抽象 第12章 类 367 12.1 类的定义和声明 368 12.1.1 类定义:扼要重述 368 12.1.2 数据抽象和封装 369 12.1.3 关于类定义的更多内容 372 12.1.4 类声明与类定义 374 12.1.5 类对象 375 12.2 隐含的this指针 376 12.3 类作用域 380 类作用域中的名字查找 382 12.4 构造函数 385 12.4.1 构造函数初始化式 387 12.4.2 默认实参与构造函数 391 12.4.3 默认构造函数 392 12.4.4 隐式类类型转换 393 12.4.5 类成员的显式初始化 396 12.5 友元 396 12.6 static类成员 398 12.6.1 static成员函数 400 12.6.2 static数据成员 400 小结 403 术语 403 第13章 复制控制 405 13.1 复制构造函数 406 13.1.1 合成的复制构造函数 409 13.1.2 定义自己的复制构造函数 409 13.1.3 禁止复制 410 13.2 赋值操作符 411 13.3 析构函数 412 13.4 消息处理示例 415 13.5 管理指针成员 419 13.5.1 定义智能指针类 421 13.5.2 定义值型类 425 小结 427 术语 427 第14章 重载操作符与转换 429 14.1 重载操作符的定义 430 14.2 输入和输出操作符 435 14.2.1 输出操作符<>的重载 437 14.3 算术操作符和关系操作符 439 14.3.1 相等操作符 440 14.3.2 关系操作符 441 14.4 赋值操作符 441 14.5 下标操作符 442 14.6 成员访问操作符 443 14.7 自增操作符和自减操作符 446 14.8 调用操作符和函数对象 449 14.8.1 将函数对象用于标准库算法 450 14.8.2 标准库定义的函数对象 451 14.8.3 函数对象的函数适配器 453 14.9 转换与类类型 454 14.9.1 转换为什么有用 454 14.9.2 转换操作符 455 14.9.3 实参匹配和转换 458 14.9.4 重载确定和类的实参 461 14.9.5 重载、转换和操作符 464 小结 466 术语 467 第四部分 面向对象编程与泛型编程 第15章 面向对象编程 471 15.1 面向对象编程:概述 472 15.2 定义基类和派生类 473 15.2.1 定义基类 474 15.2.2 protected成员 475 15.2.3 派生类 476 15.2.4 virtual与其他成员函数 479 15.2.5 公用、私有和受保护的继承 482 15.2.6 友元关系与继承 486 15.2.7 继承与静态成员 486 15.3 转换与继承 487 15.3.1 派生类到基类的转换 487 15.3.2 基类到派生类的转换 489 15.4 构造函数和复制控制 490 15.4.1 基类构造函数和复制控制 490 15.4.2 派生类构造函数 490 15.4.3 复制控制和继承 494 15.4.4 虚析构函数 495 15.4.5 构造函数和析构函数中的虚函数 497 15.5 继承情况下的类作用域 497 15.5.1 名字查找在编译时发生 498 15.5.2 名字冲突与继承 498 15.5.3 作用域与成员函数 499 15.5.4 虚函数与作用域 500 15.6 纯虚函数 502 15.7 容器与继承 503 15.8 句柄类与继承 504 15.8.1 指针型句柄 505 15.8.2 复制未知类型 507 15.8.3 句柄的使用 508 15.9 再谈文本查询示例 511 15.9.1 面向对象的解决方案 513 15.9.2 值型句柄 514 15.9.3 Query_base类 515 15.9.4 Query句柄类 516 15.9.5 派生类 518 15.9.6 eval函数 520 小结 522 术语 523 第16章 模板与泛型编程 525 16.1 模板定义 526 16.1.1 定义函数模板 526 16.1.2 定义类模板 528 16.1.3 模板形参 529 16.1.4 模板类型形参 531 16.1.5 非类型模板形参 533 16.1.6 编写泛型程序 534 16.2 实例化 535 16.2.1 模板实参推断 537 16.2.2 函数模板的显式实参 540 16.3 模板编译模型 542 16.4 类模板成员 545 16.4.1 类模板成员函数 548 16.4.2 非类型形参的模板实参 551 16.4.3 类模板中的友元声明 552 16.4.4 Queue和QueueItem的友元 声明 554 16.4.5 成员模板 556 16.4.6 完整的Queue类 558 16.4.7 类模板的static成员 559 16.5 一个泛型句柄类 560 16.5.1 定义句柄类 561 16.5.2 使用句柄 562 16.6 模板特化 564 16.6.1 函数模板的特化 565 16.6.2 类模板的特化 567 16.6.3 特化成员而不特化类 569 16.6.4 类模板的部分特化 570 16.7 重载与函数模板 570 小结 573 术语 574 第五部分 高级主题 第17章 用于大型程序的工具 579 17.1 异常处理 580 17.1.1 抛出类类型的异常 581 17.1.2 栈展开 582 17.1.3 捕获异常 583 17.1.4 重新抛出 585 17.1.5 捕获所有异常的处理代码 586 17.1.6 函数测试块与构造函数 586 17.1.7 异常类层次 587 17.1.8 自动资源释放 589 17.1.9 auto_ptr类 591 17.1.10 异常说明 595 17.1.11 函数指针的异常说明 598 17.2 命名空间 599 17.2.1 命名空间的定义 599 17.2.2 嵌套命名空间 603 17.2.3 未命名的命名空间 604 17.2.4 命名空间成员的使用 606 17.2.5 类、命名空间和作用域 609 17.2.6 重载与命名空间 612 17.2.7 命名空间与模板 614 17.3 多重继承与虚继承 614 17.3.1 多重继承 615 17.3.2 转换与多个基类 617 17.3.3 多重继承派生类的复制控制 619 17.3.4 多重继承下的类作用域 620 17.3.5 虚继承 622 17.3.6 虚基类的声明 624 17.3.7 特殊的初始化语义 625 小结 628 术语 628 第18章 特殊工具与技术 631 18.1 优化内存分配 632 18.1.1 C++中的内存分配 632 18.1.2 allocator类 633 18.1.3 operator new函数和 operator delete函数 636 18.1.4 定位new表达式 638 18.1.5 显式析构函数的调用 639 18.1.6 类特定的new和delete 639 18.1.7 一个内存分配器基类 641 18.2 运行时类型识别 646 18.2.1 dynamic_cast操作符 647 18.2.2 typeid操作符 649 18.2.3 RTTI的使用 650 18.2.4 type_info类 652 18.3 类成员的指针 653 18.3.1 声明成员指针 653 18.3.2 使用类成员的指针 655 18.4 嵌套类 658 18.4.1 嵌套类的实现 658 18.4.2 嵌套类作用域中的名字查找 661 18.5 联合:节省空间的类 662 18.6 局部类 665 18.7 固有的不可移植的特征 666 18.7.1 位域 666 18.7.2 volatile限定符 668 18.7.3 链接指示:extern "C" 669 小结 672 术语 673 附录 标准库 675 索引 703
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值