达内2013C++教程STL笔记

一,day1-day7:

1. 可以直接比较string对象的大小,而c风格字符串比较的只是指针大小
 例子1:
    string a[] = { "ab", "cd" };
    if( a[1] > a[0] ){ cout << "yes" }  //将输出yes
    const char *ch[] = { "good", "test", "nihao" };  
    ch[0] > ch[1] // 这里比较的只是指针大小,而不是比较字符串的大小
 例子2:

template <class V>
bool lessthan( const V &lh, const V &rh )
{
   return lh < rh;
}
bool lessthan( const char* &lh, const char* &rh )
{
   return strcmp( lh, rh ) < 0;
}

template <class T >
void sort( T a[], int len )
{
    for( int i = 0; i < len - 1; i++ )
    {
       for( int j = i + 1; j < len; j++ )
       {
           if( lessthan( a[i], a[j] ) // 实例化lessthan函数时如果已经有对应的函数,则不会再实例化模板了
           {
              swap( a[i], a[j] ); 
           }
       }
    }

}

template <class T>
void display( T a[], int len )
{
   for( int i = 0; i < len; i++ )
   {
      cout << a[i] << " ";
   }
}
string d[] = { "cd", "ab", "kn", "gk" };
const char *ch = { "good", "morning", "nice", "to" };
sort( d, 4 );
display( d, 4 );  //可以对string数组进行排序
sort( d, 4 );  // 此时也可以对c风格字符串进行排序了
注:编译器会优先选择最匹配的特殊化版本来使用,在没有更匹配的特殊化版本才选用
一般化的版本类实例化。

2. 每个模板需要一个模板头,模板头格式
   template <typename T, ... >
   其中<>里面的是模板形参,一般是类型,也可以是非类型形参(整型形参,在模板里面作为整型常量)

3. 模板,排序,特殊化
#include <iostream>
#include <string>
#include <typeinfo>
using namespace std;
template <class V>
bool lessthan( const V &lh, const V &rh )// 模板1
{
    return lh > rh;
}

template <>  //  对C风格字符串常量的特殊化,为全特化,相当于V对应于const char*,此为模板,不调用时不产生代码,而函数则会有代码
bool lessthan( const char *const &lh, const char *const &rh ) // 模板2,都要写const,要与上面那个模板lessthan保持一致(上面模板1参数为const类型,所以这里也要在参数前加const),即在一致的情况下更加具体,才会实例化该具体模板                           
{
   return strcmp( lh, rh ) > 0;
}

/*或者下面的也行:
template <class V>
bool lessthan( V &lh, V &rh ) 
{
   return lh > rh;
}

template <>  //  对C风格字符串常量的特殊化,为全特化,相当于V对应于const char*,此为模板,不调用时不产生代码,而函数则会有代码
bool lessthan( const char * &lh, const char * &rh ) //  都要写const,要与上面那个模板lessthan保持一致(上面模板1参数为const类型,所以这里也要在参数前加const),即在一致的情况下更加具体,才会实例化该具体模板                                                     
{                                               
   return strcmp( lh, rh ) > 0;
}

注:模板重载,一个更加具体的模板必须在与另一个模板保持一致的情况下更加具体,例如上面的模板2在与模板1保持一致的情况下
更加具体,模板1实例化const char*时V要实例化为char*,而模板2则直接用
*/
template <class T >
void sort( T a[], int len )
{
    for( int i = 0; i < len - 1; i++ )
    {
        for( int j = i + 1; j < len; j++ )
        {
           if( lessthan( a[i], a[j] ) )
           {
               swap( a[i], a[j] ); 
           }
        }
    }

}
template <class T>
void display( T a[], int len )
{
    for( int i = 0; i < len; i++ )
    {
       cout << a[i] << " ";
    }
    cout << endl;
}

int main()
{
   int a[] = { 2, 3, 1, 6, 5, 4 };
   char b[] = { 'a', 'n', 'c' };
   double c[] = { 2.3, 1.4, 4.2, 3.3 };
   string d[] = { "cd", "ab", "kn", "gk" };
   const char* ch[] = { "nice", "to", "meet", "you" };
   sort( a, 6 );
   display( a, 6 );
   sort( b, 3 );
   display( b, 3 );
   sort( c, 4 );
   display( c, 4 );
   sort( d, 4 );
   display( d, 4 );
   sort( ch, 4 );
   display( ch, 4 );

   // cout << endl << typeid(const char*).name() << endl;
   system("pause");
   return 0;

} 

4. 偏特化,模板中打印传过来的数组
#include <iostream>
using namespace std;
template <class T>
void print( const T &d )
{
   cout << d << ' '; 
   return;
}

template < class T, int N >
void print( T(&a)[N] )  // 用于数组的特殊化版本,可以传递一维或多维数组,在此为偏特化 
{
   for( int i = 0; i < N; i++ )
   {
      print(a[i]); 
   }
   return;
}

int main()
{
   int arry[] = { 2, 3, 4 };
   int b[3][2] = { 11, 22, 33, 44, 55, 66 };
   print(arry); // 输出2 3 4
   cout << endl;
   print(b);  // 输出11 22 33 44 55 66
   system("pause");
   return 0;
}

5. 全特化、偏特化
#include <iostream>
using namespace std;

template <typename T>
void print( const T &d )  
{
   cout << d << ' '; 
   return;
}

template <typename T>
void print( T *p )
{
   cout << *p << endl;
   return;
}

template < class T, int N > // 模板1
void print( T(&a)[N] )  // 用于数组的特殊化版本,可以传递一维或多维数组,在此为偏特化 
{
   for( int i = 0; i < N; i++ )
   {
      print(a[i]); 
   }
   return;
}

template <> // C风格字符串的特殊化,在此为全特化 // 模板2
void print( const char *s )
{
   cout << s;
   return;
}

int main()
{
   print("hello");  // vs2010,则出错:“print”: 对重载函数的调用不明确,其不知道实例化模板1还是模板2                 
   system("pause");
   return 0;
} 

6. 
#include <iostream>
using namespace std;
template <typename K, typename V>
struct Pair
{
   K first;
   V second;
   void show(){ cout << first << ' ' << second << endl; }
};

template <typename K, typename V>
Pair<K, V> makepair( K x, V y )
{
   Pair<K, V> pkv = { x, y };
   return pkv;
}

int main()
{
   makepair( 1, "hello" ).show(); // 根据数据产生pair

   system("pause");
   return 0;
} 
注:特殊化有几种层次
偏特化:带某种偏向的特殊化,比如偏向指针的,偏向数组的;
全特化:模板头的尖括号是空的,全部模板形参都确定下来了,类似于普通的函数
和类,但只有在程序中某处进行了实例化才会在最终的可执行文件中产生代码;
部分特化:模板的部分形参确定下来了。

7. 成员函数模板
#include <iostream>
#include <string>
using namespace std;
template <class K, class V>
struct Pair
{
   K first;
   V second;
public:
   Pair():first(), second(){}

template <class F, class Q> // 成员函数模板 
Pair( const F &x, const Q &y ):first(x), second(y){}

template <typename I, typename J> // 为了保证不同Pair实例化对象之间的赋值,要为模板,如Pair<int,double>类型赋值给Pair<char, double>类型 
Pair& operator=( const Pair<I, J> &value )
{
   first = value.first;
   second = value.second;
   return *this;
}
void show()
{
   cout << first << ' ' << second << endl; 
   return;
} 
friend ostream& operator<<( ostream &os, const Pair &v )
{
   return os << v.first << ' ' << v.second << endl;
} 


};

int main()
{
   Pair<int, double> pid1( 1, 2.3 ); // 'a'不是int类型,2不是double类型,但可以转换为对应的类型 
   Pair<char, int> pid2( 'a', 30 );  // 这里传的数据并不一定是对应的int 和double类型,所以上面的构造函数要为模板成员函数
   //pid1.show();
   cout << pid1;
   pid1 = pid2;    // 若没有上面定义的赋值模板,则该语句编译不过 
   //pid1.show();
   cout << pid1;               
   char a[10] = "aa";
   char b[10] = "oo";
   Pair<const char*, const char*> pcc(a, b ); // pcc对象中存的是两个地址 
   Pair<string, string> pss;// 保存的是字符串
   pss = pcc; // pss会把pcc中的数据保存一份
   cout << pss;  // 输出aa oo
   cout << pcc;
   strcpy( a, "cc" );
   strcpy( b, "pp" );
   cout << "\nafter copy\n";
   cout << pcc;  // 输出cc pp
   cout << pss; // 输出aa oo
   system("pause");
   return 0;
} 

8.  友元函数模板、const对象不能调用普通成员函数,只能调用const成员函数
#include <iostream>
using namespace std;
template < typename T, int N >
class Array
{
private:
   T a[N];
public:
   T& operator[]( int index ) // 函数1 const的Array对象可以调用此函数,也可以调用下面的函数2
   {
      return a[index];
   }


const T& operator[]( int index )const // 函数2,对应const的Array对象只能调用该函数,因为只有const函数能保证不修改对象
{
   return a[index];
}

void fill( T start, const T &step )
{
   for( int i = 0; i < N; i++, start += step )
   {
      a[i] = start;
   }


   return;
}

template <typename A, int M > //友元函数模板
friend ostream& operator<<( ostream &o, const Array<A, M> &x ); // 尽量不要将声明与定义分开写
};

template < typename A, int M > // 在外面定义友元函数模板
ostream& operator<<( ostream &o, const Array<A, M> &x )
{
   o << "[ ";
   for( int i = 0; i < M; i++ )
   {
      o << x[i] << ' '; // const的x对象将调用函数2
   }
   o << "] ";
   return o;
}


int main()
{
   Array< int, 5 > a1;
   a1.fill( 11, 2 );
   cout << a1 << endl; // 输出[ 11 13 15 17 19 ]
   system("pause");
   return 0;
}
/*
注:
函数1与函数2重载,成员函数默认会传递一个对象的地址给this,故函数1的完整参数列表为:
(int index, Array *const this),而函数2的完成参数列表为:
(int index, const Array *const this),故参数列表不同,可以构成函数重载
*/

9. 一个练习的例子
/*
写一个函数模板,从一个数组中查找第一个等于指定值的元素,返回它的地址
main中定义char数组,double数组,string数组,C风格字符串数组,日期
数组,分别用这个模板来查找一个数据
*/
#include <iostream>
#include <string>
using namespace std;

typedef struct Date
{
   int year;
   int month;
   int day;
   friend ostream& operator<<( ostream &os, const Date& d )
   {
      return os << d.year << '-' << d.month << '-' << d.day;
   }
}Date;

template < typename V >
bool isEqual( const V left, const V right ) // 模板1
{
   if( left == right )
   {
      return true;
   }
   return false;
}
template <>
bool isEqual( const char* left, const char* right ) // 模板2,模板2与模板1构成模板重载
{
   if( strcmp( left, right ) == 0 )
   {
      return true;
   }
   return false;
}

template <>
bool isEqual( Date left, Date right ) // 模板2,模板2与模板1构成模板重载
{
   if( left.month == right.month && left.year == right.year && left.day == right.day )
   {
      return true;
   }
   return false;
}

template < typename T >
T* find( T a[], int len, T value ) // 模板1
{
   for( int i = 0; i < len; i++ )
   {
      if( isEqual( a[i], value ) )
      {
         return &a[i];
      }
   }
   return NULL;
}

/*
template <>  // 这样也可以,针对C风格字符串的特化
const char** find( const char *a[], int n, const char* v )// 模板2,这里特化必须要与模板1对应,且更加具体
{
   for( int i = 0; i < n; i++ )
   {
      if( strcmp( a[i], v ) == 0 )
      {
          return a + i;
      }
   }
   return NULL;
}
*/
int main()
{
   char ch[] = { 'd', 'e', 'a' };
   double d[] = { 1.1, 2.3, 4.4 };
   string str[] = { "ad", "ad", "sgg" };
   const char* cch[] = { "hello", "nice", "to" };
   Date dd[] = { {2010, 1, 1 }, { 2011, 2, 2 }, { 2013, 3, 3 } };
   cout << *find( ch, 3, 'e' ) << endl;
   cout << *find( d, 3, 2.3 ) << endl;
   string target = "sgg";
   cout << *find( str, 3, target ) << endl;
   const char** pcc = find( cch, 3, "to" );
   cout << *pcc << endl;
   Date d1 = { 2011, 2, 2 };
   cout << *find( dd, 3, d1 ) << endl;
   system("pause");
   return 0;
}


1. 迭代器:封装的指针,作为容器内部类型。四种:

   iterator,const_iterator,reverse_iterator,const_reverse_iterator


2. 容器会自己保存一份数据,因此自定义类型的数据要保存在容器中,应该支持拷贝构造和赋值运算符以及无参构造
    int a[] = { 2, 3, 4, 5, 6, 1, 8, 9 }; 
    sort( a, a + 8 );
    cout << endl;
    list<int> lt( a, a + 8 ); //lt自己会保存一份数据


3. 插入数据时,可能会释放空间,所以原来的迭代器在插入或删除数据之后很可能失效了,应该重新取得迭代器。
   它是随机迭代器(可以加上一个整数的),当然也是双向迭代器(可以++和--)。


4. STL和容器
 4.1 STL
 4.2 标准容器:类模板
     序列式容器:vector,deque,list
     关联式容器:set数据集,multiset多重数据集,map映射,multimap多重映射

 4.3 容器适配器:对容器的再包装,进行了访问限制
     stack栈:先进后出
     queue队列:先进先出
     priority_queue优先队列:最大的先出

 4.4 通用算法:约70种


你可能感兴趣的:(达内2013C++教程STL笔记)