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

    技术2022-05-12  13

    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)