1,模板的实例化:
模板可以看成和类一样,必须实例化才能使用 模板本身不是类名 带上尖括号 才是类名
// 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,函数模板不支持模板形参默认值
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"); 此处调用函数模板,常量字符窗用 < 比较的是常量字符串的地址,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是随机迭代器,其他都是双向迭代器