【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)//默认将容器中的数取反,1001,也可以将特定位置取反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;}


 

这个程序有一个缺点,就是不能进行隐式转换,因为模板没有提供这些转换。

 

0 0
原创粉丝点击