C++模板

来源:互联网 发布:杭州创业软件his 编辑:程序博客网 时间:2024/06/03 21:57

1,模板的实例化:

      模板可以看成和类一样,必须实例化才能使用     模板本身不是类名    带上尖括号 才是类名

      stack<int> s;  stack<double> d; stack<int> c;
      // s = d;  错误
      s = c; 

2,模板里面也可以有非类型的参数,原则上只能是整数,而且是字面量    字面量在编译时就确定下来。  也可以有默认值

      template <typename T, int len>   
      class Stack{};
      Stack<char, 20> s;    //  非模板参数,必须是字符面值


3,模板里面的参数可以有默认值

      template <typename T=int, int len=30>   
      class Stack{};
      Stack<> s;    //  s 实为 Stack<int, 30>

      Stack<float> f;    // f 实为 Stack<float, 30>

4,模板特化

      一般的类型使用模板就可以搞定,特殊的类型就使用特化,比如const char*作为模板实参时,就需要特化版本

     想查看实际替换时是什么类型,这就需要运行时类型识别函数了typeid

     特化版本中和普通版本中可以不一样

     template <typename To, typename From>
     To convertto(From v) { return To(v); }
     template <typename To>
     To convertto(const char* str) { return To(atof(str)); }    

    template <typename T, typename U>
    struct Pair
    {
        T first;
        U second;
        Pair():first(),second(){}
        Pair(const T& a, const U& b):first(a),second(b){}

        template<typename X, typename Y>
        Pair& operator=(const Pair<X,Y>& p){
             first = convertto<T>(p.first);
             second = convertto<U>(p.second);
             return *this;
        }
    };

     Pair<const char*,const char*> b("78","90.5");
    a = b;   // 此处将const char* 转换成double 需要单独拿出来,所以需要对const char* 做特化版本


    template < typename T=int, int len=10 >
    class Stack{
         T a[len];
         int cur;

         ...

    };

    template <int len>
    class Stack<const char*,len>{    //部分特化
         string a[len];
         int cur;

         ....

    };

5,最好不要把模板的声明与定义分开,这样很麻烦

    
#include <iostream>#include <string>#include <typeinfo>   // 用于打印类型信息using namespace std;template <int n>class Fact {public:    enum { val=Fact<n-1>::val*n};};template <>class Fact <0> {public:    enum { val=1};};template < typename T>class Type {public:   static string name() { return typeid(T).name(); }};template <>class Type <char> {    //特化public:   static const char* const name;};const char* const Type<char>::name="char";template <>class Type<int> {     public:   static string name() {  return "int";  }   // 可以用于代替系统的typeid函数的输出};template < typename T>class Type<T*> {    // 偏特化public:   static string name() { return Type<T>::name()+" pointer"; }};int main(){   cout << Fact<2>::val << endl;  // 用模板实现递归   cout << Type<double>::name() << endl;   cout << Type<char>::name << endl;   cout << Type<int*>::name() << endl;   cout << Type<double**>::name() << endl;}
 
6,对于函数模板,可以根据实参推测形参类型名

     template < typename T >
     string type(T t)
     {
         return Type<T>::name(); // 见上
     }
     int main()
     {
        cout << type(123) << endl;   // 对于函数模板,可以根据实参推测形参类型名
        cout << type(45.6) << endl;
        int a=10;
        char b='k';
        cout << type(a/1.0) << endl;  // double
        cout << type(a<b) << endl;    // bool
        cout << type<&a> << endl;    // int pointer
     }


7,函数模板不支持模板形参默认值

template < typename T, int N>   //函数模板不支持模板形参默认值
void show(T(&t)[N])  // 偏特化

void show(T*(&t)[N])  // 指针数组的偏特化

8,函数模板与同名的非模板函数可以重载,这种情况下,调用时先找参数完全匹配的非模板函数,  

      如果找不到就调用匹配的模板函数。

template <typename T>
const T& Min(const T& a, const T& b)
{
      return a<b?a:b;

}

const char* Min(const char* a, const char* b)

{    retrun strcmp(a,b)<0?a:b;   }

Min("hello", "world");     此处会优先调用普通函数,返回hello
指明用模板   Min<>("hello", "world");    此处调用函数模板,常量字符窗用 < 比较的是常量字符串的地址,if ("hello<world") 比较的是地址,可以通过  printf("%p    %p\n", "hello", "world");输出地址进行验证,或者说可以查看标准库对字符串常量小于符号的重载???

9,模板只有真正用到的时候才会产生对应的代码


10,函数模板也可以重载

#include <iostream>using namespace std;template <typename T>T Max(T x, T y) { return x>y?x:y; }template <typename T>T Max(T a, T b, T c) {   T t; t=(a>b)?a:b;   return (t>c)?t:c; }int main() {   int m=10, n=20;   double a=10.1, b=20.2, c=30.3; // cout << max(m,n) << endl;   cout << Max(m,n) << endl;   cout << Max(a,b,c) << endl;   return 0;}



11,
要是将临时变量作为参数,而且这个参数是引用类型参数,则这个引用参数必须是生命为const类型


/*                 用模板实现一个简单的autoptr                   */#include <iostream>using namespace std;template <typename T>class autoptr{T* p;public:autoptr(T* p):p(p){}~autoptr(){delete p;}autoptr(autoptr& a):p(0){operator=(a);}autoptr& operator=(autoptr& a){   // 返回引用,可以连续赋值if(this==&a) return *this;if(p!=NULL) delete p;p = a.p;a.p = NULL;return *this;}T& operator*()const{return *p;}T* operator->()const{return p;}};class A{int data;public:A(int d):data(d){cout<<this<<"A("<<d<<")"<<endl;}~A(){cout<<this<<"~A()"<<data<<endl;}void show()const{cout<<this<<":"<<data<<endl;}};int main(){autoptr<A> p(new A(10));p->show();autoptr<A> q(p);//p->show();出错,p已经没有动态内存的所有权了q->show();autoptr<A> r(new A(20));(*r).show();r = q;}

12,模板最适合做数据结构与算法,类模板与函数模板配合使用

/*           用模板实现sort函数            */#include <iostream>#include <cstring>#include <algorithm>using namespace std;template <typename T>void sort(T a[], int n){   for(int i=0; i<n; i++) {      int min=i;      for(int j=i+1; j<n; j++) {         if(a[j]<a[min])            min = j;      }      swap(a[i],a[min]);   }}template <>void sort(const char* a[], int n) {   for(int i=0; i<n; i++) {      int min=i;      for(int j=i+1; j<n; j++) {         if(strcmp(a[j],a[min])<0)            min = j;      }      swap(a[min],a[i]);   }}template <typename T>void sort(T* a[], int n) {   for(int i=0; i<n; i++) {      int min=i;      for(int j=i+1; j<n; j++)         if(*a[j]<*a[min])            min = j;      swap(a[min],a[i]);   }}struct Date {   int y, m, d;};bool operator<(const Date& a, const Date& b) {   return (a.y<b.y||a.y==b.y&&(a.m<b.m||a.m==b.m&&a.d<b.d));}ostream& operator<<(ostream& o, const Date& d) {   return o << d.y << '-' << d.m << '-' << d.d << endl;}template <typename T>void show(T a[], int n) {   cout << "show T a[], int n" << endl;   for(int i=0; i<n; i++)      cout<< a[i] << ' ';   cout << endl;}template <typename T, int N>void show(T(&t)[N]) {    // 编译器可以通过 sizeof(t)/sizeof(T) 求出 N   cout << "show(T(&t)[N])" << endl;   for(int i=0; i<N; i++)      cout << t[i] << ' ';   cout << endl;}template <typename T, int N>void show(T*(&t)[N]) {   for(int i=0; i<N; i++)      cout << *t[i] << ' ';   cout << endl;}template <int N>void show(const char*(&t)[N]) {   for(int i=0; i<N; i++)      cout << t[i] << ' ';   cout << endl;}template <typename T>void show(T data) {   cout << data << endl;   }int main() {   double m=123.4;   show(m);   int a[5]={1,2,3,4,5};   double d[4]={1.1,2.2,3.3,4.4};   Date x[3]={{2010,9,30}, {2010,8,8}, {2010,9,9}};   sort(a, 5);   sort(d, 4);   sort(x, 3);   show(a,5); show(d,4); show(x,3);   show(a);show(d);show(x);   const char* s[3]={"furong","qiamg","miss"};   sort(s,3);   show(s);   int* ap[2]={new int(5), new int(3)};   double* bp[2]={new double(1.1), new double(2.2)};   sort(ap,2); sort(bp,2);   show(ap); show(bp);} 



-------------------------------------------------------------------------------------------
day 02  AM  容器,序列
-------------------------------------------------------------------------------------------


1,template <class A, class B, class C>
   A func(B, C) {}
   
   func<int>(...,...)   // 通过<>指定返回值类型,参数可以自动判断类型,但是返回值不可以,,需要手动实例化
// 或者通过参数B,C类型可以退出返回值类型

2,STL
1,类模板(容器)container
标准容器,
序列式容器
vector 不适合插入删除,只适合在末尾插入删除
vector<bool> 每个元素只占一个位
deque 双端队列 通过多个vector组合而成
list 双端列表 
关联式容器 (二叉查找树实现)
set,multiset
map,multimap
迭代器 iterator(每种标准容器都会提供一个迭代器内部类型,封装指针
支持*,->,++,==,!=)
函数对象functor,
容器适配器(特殊容器)
stack
queue
priority_queue
内存分配器 allocator
2,函数模板(通用算法) algorithm 


3,标准库里面如果成员函数和通用算法都能实现某功能,最好用专用的成员函数,效率更高


4,sort函数只适用于用数组实现的容器:数组,vector,deque
   关联式容器不需要sort,list容器自带sort
   
////////////////////////////////////////////////////////////////////////////////////
5,标准容器(类模板)共性:
   构造函数(包括无参构造,拷贝构造,区间构造(两个迭代器表示的两个位置))
   析构函数
   迭代器相关函数:.begin()正向iterator,返回指向第一个元素位置的迭代器 
  .end()返回指向超越最后一个位置的迭代器
  .rbegin()反向reverse_iterator,反向迭代器
  .rend()
   标准都支持 * -> == ++ -- = !=
   插入:.insert(pos,element)  其中pos是个表示位置的迭代器
   删除:.erase(pos), .erase(pos_beg,pos_end)
   清除:.clear() 清除容器里所以的数据
   大小:.size()  .max_size()
   交换:.swap(c2)  .swap(c1,c2)
   运算:= > < >= <= == !=
6,序列式容器的共性:vector,deque,list
构造函数:指定元素个数和初始值(初始值默认为零初始化)
插入:.insert(pos,n,element), insert(pos,pos_beg,pos_end)
赋值:.assign(n,element), assign(pos_beg,pos_end)
调整大小:.resize(n,element=零初始化);   增加的元素零初始化
首尾:.front(), .back() 返回的是引用,即可以修改
增删:.push_back(element), .pop_back() 只删除,返回void

deque< vector<int> >,  新标准可以不加空格deque<vector<int>>

1,vector的个性:
当前容量:.capacity()
约定容量:.reserver(n)
下标[]:.operator[](i) 不考虑越界,  .at(i) 越界时会抛出异常
template < typename T >
void show(T a[], int n)
template < typename T >
void show(const vector<T>& v);  // 不用传大小

vector迭代器传入删除数据之后可能会失效,因为可能会重新分配内存 1,2,4,8,16

通过[] 和 at(i) 也可以赋值

int m=3, n=5;
vector< vector<int> > vvi(m, vector<int>(n)); // 二维数组
vector< vector<int> > ivv;
vvi.resize(m+3);

2,deque的个性:double-ended queue
下标[]:.operator[](i) 不检查越界,.at(i) 越界抛异常
增删:.push_front(element),.pop_front()

3,list个性:双向链表
增删:.push_front(element),.pop_front(),
 .remove(element) 删除等于element的所有元素,需要==运算符
 .remove_if()
不支持下标[]
去除相邻的重复:.unique() 只保留一个  // 也可以指定比较规则
// 如果想要不管相邻是否,重复的都只保留一个,可以先排序
排序:.sort(compare_func=less)默认用小于符号比较,从小到大排序
倒置:.reverse()点到链表中元素顺序
转移:.splice(pos, list2)   // 把另一个链表的数据转移过来,另一个链表变空
 .splice(pos, list2, pos2)   .splice(pos, list2, pos_beg, pos_end)
归并:.merge(list2)  // list2将变空

li.sort(greater<int>());

7,关联式容器共性:都是二叉查找树实现,都自动根据关键字排序
查找:.find(key) 返回迭代器,指向找到的第一个元素,没找到返回 .end()
统计:.count(key) 返回关键字等于 key 的个数
删除:.erase(key) 删除关键字等于 key 的所有元素
区间:.lower_bound(key), .upper_bounder(key)   半开半闭
 .equal_range(key), 一次取得关键字为key的元素的区间,返回一个pair
插入:.insert(element)
构造函数:可以用比较函数做参数 bool compare(K a, K b);


-------------------------------------------------------------------------------------------
day 03  AM  容器,序列
-------------------------------------------------------------------------------------------


1,map的个性:
不允许key重复
支持以key为下标访问对应的value的引用,如果key不存在就新增一个元素,以这个为key

map.insert(pair<int,string>(1,"hello"));
map.insert(make_pair(1,"hello"));
map.insert(map<int.string>::value_type(1,"hello"));
mis[3] = "world";

2,multimap的个性:
允许重复key
不支持下标[]

chenzq@tarena.com.cn
chenzongquan@hotmail.com

3,迭代器分类:
1,输入迭代器:可读*it的值,但不一定能修改*it的值
2,输出迭代器:可以设置*it的值,但不一定能读取*it的值
3,前向迭代器:可以读取也可以设置*it的值
4,双向迭代器:支持--
5,随机迭代器:几乎和指针一样,支持--,+n,-n,比较大小,下标[]

vector和deque是随机迭代器,其他都是双向迭代器
0 0
原创粉丝点击