【ThinkingInC++】73、深入理解模板
来源:互联网 发布:南方电网待遇 知乎 编辑:程序博客网 时间:2024/05/21 06:20
第五章 深入理解模板
5.1 模板参数
关于bitset
bitset就是可以存放二进制的容器。
对于bitset的主要操作有:
(constructor)
Construct bitset (public member function) //构造bitset.. 格式bitset<长度> 名字
applicable operators
Bitset operators (functions) //可以直接对bitset容器进行二进制操作,如^,|,~,<<,>>等等
operator[]
Access bit (public member function) //可以用如数组形式的赋值。bitset<4> b; b[0]=1;
set
Set bits (public member function)//默认将容器中所有值赋为1,也可以将特定的位置赋给特定的值
如 bitset<4> b; b.set(); //1111. b.set(2,0) // 1011.
reset
Reset bits (public member function) //默认将容器中所有值赋值为0,也可以将特定位置赋特定的值
flip
Flip bits (public member function)//默认将容器中的数取反,1变0,0变1,也可以将特定位置取反bitset<4>b(string ("0001")); b.file(2); // 0101; b.file(); //1010
to_ulong
Convert to unsigned longinteger (public member function) //将容器的值转化成10进制的数
to_string
Convert to string (public member function) //将容器累的值转为字符串
count
Count bits set (public member function) //统计容器中1的个数
size
Return size (public member function) //容器的大小
test
Return bit value (public member function) //返回每个位置上的数
any
Test if any bit is set (public member function) //容器的值>0返回真,反之。
none
Test if no bit is set (public member function) //和any取反。容器的值==0返回真。反之
表3-7 bitset操作
b.any()
b中是否存在置为1的二进制位?
b.none()
b中不存在置为1的二进制位吗?
b.count()
b中置为1的二进制位的个数
b.size()
b中二进制位的个数
b[pos]
访问b中在pos处的二进制位
b.test(pos)
b中在pos处的二进制位是否为1?
b.set()
把b中所有二进制位都置为1
b.set(pos)
把b中在pos处的二进制位置为1
b.reset()
把b中所有二进制位都置为0
b.reset(pos)
把b中在pos处的二进制位置为0
b.flip()
把b中所有二进制位逐位取反
b.flip(pos)
把b中在pos处的二进制位取反
b.to_ulong()
用b中同样的二进制位返回一个unsigned long值
os << b
把b中的位集输出到os流
Urand.h
/*** 书本:【ThinkingInC++】* 功能:关于模板bitset* 时间:2014年10月16日16:28:59* 作者:cutter_point*/#ifndef URAND_H_INCLUDED#define URAND_H_INCLUDED#include <bitset>#include <cstddef>#include <cstdlib>#include <ctime>using std::size_t;using std::bitset;template<size_t UpperBound> class Urand{ bitset<UpperBound> used;public: Urand() { srand(time(0)); } //产生随机数 size_t operator()();};template<size_t UpperBound>inline size_t Urand<UpperBound>::operator()(){ if(used.count() == UpperBound)//b.count()b中置为1的二进制位的个数 used.reset(); //说明已经满了,里面为1的个数满了,全为1,那么这个让他全部重置为0 size_t newval; while(used[newval = rand()%UpperBound]) ; //找到唯一的数值,就是为假的时候跳出来 used[newval]=true; return newval;}#endif // URAND_H_INCLUDED
UrandTest.cpp
/*** 书本:【ThinkingInC++】* 功能:关于如何使用Urand.h* 时间:2014年10月16日16:29:21* 作者:cutter_point*/#include <iostream>#include "Urand.h"using namespace std;int main(){ Urand<10> u; for(int i=0 ; i < 20 ; ++i) cout<<u()<<' '; system("pause"); return 0;}
5.1.3 模板类型的模板参数
TempTemp.cpp
/*** 书本:【ThinkingInC++】* 功能:关于模板类型的模板参数* 时间:2014年10月16日16:29:58* 作者:cutter_point*/#include <cstddef>#include <iostream>using namespace std;template<class T>class Array{ enum { INIT=10 }; //美剧类型,这是为了避免使用宏 T* data; size_t capacity; size_t count;public: Array() { count=0; data=new T[capacity = INIT]; //创建一个T类型的数组 } ~Array() { delete [] data; } void push_back(const T& t) { if(count == capacity) //如果数量和总的个数一样,说明内存不够 { size_t newCap=2*capacity; //把内存空间增加一倍 T* newData=new T[newCap]; //把原来的数据移动到新的数组上 for(int i=0 ; i<count ; ++i) newData[i]=data[i]; //回收旧的空间 delete []data; //更新数据成员 data=newData; capacity=newCap; } //如果还有空余空间 data[count++]=t; //把t赋值给最后的一个空闲的地方 } void pop_back() { if(count > 0) --count; //把最后一位去掉 } T* begin() { return data; } T* end() { return data+count; }};template<class T, template<class> class Seq> //这里可以这样写template<class T, template<class U> class Seq>class Container{ Seq<T> seq; //参照上面那个模板,这里seq就相当于Tpublic: void append(const T& t) { seq.push_back(t); } T* begin() { return seq.begin(); } T* end() { return seq.end(); }};int main(){ Container<int, Array> container; //template<class T> class Array 这里seq就相当于Array container.append(1); container.append(2); int* p=container.begin(); while(p != container.end()) cout<<*p++<<endl; return 0;}
关于typename的探讨
typename
"typename
"是一个C++程序设计语言中的关键字。当用于泛型编程时是另一术语"class
"的同义词。[1]这个关键字用于指出模板声明(或定义)中的非独立名称(dependent names)是类型名,而非变量名。以下是对于泛型编程中typename
两种迥然不同的用法的解释。
class
关键字的同义词
这是一项C++编程语言的泛型编程(或曰“模板编程”)的功能,typename
关键字用于引入一个模板参数,例如:
// 定义一个返回参数中较大者的通用函数
template <typename T>
const T& max(const T& x, const T& y)
{
if (y < x){
return x;
}
return y;
}
这种情况下,typename
可用另一个等效的关键字class
代替,如下代码片段所示:
// 定义一个返回参数中较大者的通用函数
template <class T>
const T& max(const T& x, const T& y)
{
if (y < x) {
return x;
}
return y;
}
以上两段代码没有功能上的区别。
类型名指示符
考虑下面的错误代码:
template <typename T>
void foo(const T& t)
{
// 声明一个指向某个类型为T::bar的对象的指针
T::bar * p;
}
struct StructWithBarAsType {
typedef int bar;
};
int main() {
StructWithBarAsType x;
foo(x);
}
这段代码看起来能通过编译,但是事实上这段代码并不正确。因为编译器并不知道T::bar
究竟是一个类型的名字还是一个某个变量的名字。究其根本,造成这种歧义的原因在于,编译器不明白T::bar
到底是不是“模板参数的非独立名字”,简称“非独立名字”。[2]注意,任何含有名为“bar
”的项的类T,都可以被当作模板参数传入foo()
函数,包括typedef
类型、枚举类型或者变量等。
为了消除歧义,C++语言标准规定:
A name used in a template declaration or definition andthat is dependent on a template-parameter is assumed not to name a type unlessthe applicable name lookup finds a type name or the name is qualified by thekeyword typename.
意即出现上述歧义时,编译器将自动默认bar为一个变量名,而不是类型名。所以上面例子中的代码 T::bar * p
会被解释为乘法,而不是声明p为指向T::bar类型的对象的指针。
如果还有另一个名为StructWithBarAsValue
类型,如下:
struct StructWithBarAsValue {
int bar;
};
那么,编译器将以完全不同的方式来解释
T::bar * p
的含义。
解决问题的最终办法,就是显式地告诉编译器,T::bar
是一个类型名。这就必须用typename
关键字,例如:
template <typename T>
void foo(const T& t)
{
// 声明一个指向某个类型为T::bar的对象的指针
typename T::bar * p;
}
这样,编译器就确定了T::bar
是一个类型名,p也就自然地被解释为指向T::bar
类型的对象的指针了。
5.4 名称查找问题
FriendScope2.cpp
/*** 书本:【ThinkingInC++】* 功能:关于模板的友元函数* 时间:2014年10月16日16:30:49* 作者:cutter_point*/#include <iostream>using namespace std;//前向声明template<class T> class Friendly;template<class T> void f(const Friendly<T>&);template<class T>class Friendly{ T t;public: Friendly(const T& theT) : t(theT) {} friend void f<> (const Friendly<T>&); //这里注意加一个<>是为了表示这是一个模板 void g() { f(*this); }};void h(){ f(Friendly<int>(1));}template<class T>void f(const Friendly<T>& fo) { cout<<fo.t<<endl; }int main(){ h(); Friendly<int>(2).g(); return 0;}
这里Friendly中的f的声明里的尖括号。这谁必须的,它告诉编译器f是一个模板。否则,编译器就会去寻找一个名为f的普通函数而不会找到他。
Box1.cpp
用模板定义操作符号
/*** 书本:【ThinkingInC++】* 功能:定义模板的操作符* 时间:2014年10月16日16:31:13* 作者:cutter_point*/#include <iostream>using namespace std;//前向声明template<class T> class Box;template<class T>Box<T> operator+(const Box<T>&, const Box<T>&);template<class T>ostream& operator<<(ostream&, const Box<T>&);//定义template<class T>class Box{ T t;public: Box(const T& theT) : t(theT) {} friend Box operator+<>(const Box<T>&, const Box<T>&); //表明这是一个模板 friend ostream& operator<< <>(ostream&, const Box<T>&); //同上};template<class T>Box<T> operator+(const Box<T>& b1, const Box<T>& b2){ return Box<T>(b1.t+b2.t);}template<class T>ostream& operator<<(ostream& os, const Box<T>& b){ return os<<'['<<b.t<<']';}int main(){ Box<int> b1(1), b2(2); cout<<b1+b2<<endl; return 0;}
这个程序有一个缺点,就是不能进行隐式转换,因为模板没有提供这些转换。
- 【ThinkingInC++】73、深入理解模板
- 【ThinkingInC++】71、深入理解字符串
- 【ThinkingInC++】68、模板介绍
- 深入理解模板
- 深入理解模板
- C++模板深入理解
- 深入理解模板1
- C++模板深入理解
- 【C++】深入理解模板
- 深入理解模板 模板元编程
- C++函数模板深入理解1
- C++类模板深入理解2
- C++编程思想(卷二):深入理解模板
- C++11变长模板解析(深入理解C++11)
- (C/C++学习笔记)函数模板的深入理解
- 从模板方法模式深入理解Java抽象类
- 深入理解
- magento开发 -- 深入理解Magento第三章 – 布局,块和模板
- php 获取上个月、下个月、本月的日期
- 【Java.IO】I/O 【字节/字符】【节点流】 - 之 - 【管道流】 - Piped*
- bind() to 0.0.0.0:80 failed (98: Address already in use)
- 第8周 项目4 个人所得税计算器
- POJ 1012 Joseph
- 【ThinkingInC++】73、深入理解模板
- MSChart 的常用属性、事件、数据源绑定
- CentOS: 开放80、22、3306端口操作
- 第8周 项目3 多分段函数求值
- iOS7下隐藏status bar的详细研究
- Week7-1圆柱体表面积
- 数组的操作push,pop,shift,unshift
- 优秀博文收集
- 第8周项目3-多分段函数求值(1)