码迷,mamicode.com
首页 > 编程语言 > 详细

【ThinkingInC++】73、深入理解模板

时间:2014-10-16 18:51:43      阅读:328      评论:0      收藏:0      [点我收藏+]

标签:thinkinginc++   模板   名称查找问题   

第五章 深入理解模板

5.1 模板参数

关于bitset

 

bitset就是可以存放二进制的容器。

对于bitset的主要操作有:

 

Construct bitset (public member function)    //构造bitset..   格式bitset<长度>  名字

Bitset operators (functions)          //可以直接对bitset容器进行二进制操作,如^,|,~,<<,>>等等

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 bits (public member function) //默认将容器中所有值赋值为0,也可以将特定位置赋特定的值

Flip bits (public member function)//默认将容器中的数取反,1001,也可以将特定位置取反bitset<4>b(string ("0001"));  b.file(2);  // 0101;  b.file();  //1010

Convert to unsigned longinteger (public member function)  //将容器的值转化成10进制的数

Convert to string (public member function)           //将容器累的值转为字符串

Count bits set (public member function)             //统计容器中1的个数

Return size (public member function)                 //容器的大小

Return bit value (public member function)           //返回每个位置上的数

any

Test if any bit is set (public member function)   //容器的值>0返回真,反之。

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就相当于T
public:
    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++   模板   名称查找问题   

原文地址:http://blog.csdn.net/cutter_point/article/details/40150449

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!