C++模板

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是随机迭代器,其他都是双向迭代器
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值