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 *);     访问

 

 

 

 

  
 

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值