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

第五章 深入理解模板

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;
}


 

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

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Bruce Eckel 《Thinking in Java》(Java编程思想)作者。Eckel有20年专业编程经验,并自1986年起教育人们如何撰写面向对象程序,足迹遍及全球,成为一位知名的 C++教师和顾问,如今兼涉Java。他是C++标准委员会拥有表决权的成员之一,曾经写过另五本面向对象编程书籍,发表过150篇以上的文章,是多本计算机杂志的专栏作家。Eckel开创Software Development Conference的C++、Java、Python等多项研讨活动。拥有应用物理学学士和计算机工程学硕士学位。 目录 译者序 前言 第1章 对象导言 第2章 对象的创建与使用 第3章 C++中的C 第4章 数据抽象 第5章 隐藏实现 第6章 初始化与清除 第7章 函数重载与默认参数 第8章 常量 第9章 内联函数 第10章 名字控制 第11章 引用和拷贝构造函数 第12章 运算符重载 第13章 动态对象创建 第14章 继承和组合 第15章 多态性和虚函数 第16章 模板介绍 附录A 编码风格 附录B 编程准则 附录C 推荐读物 索引 第2卷:实用编程技术 出版者的话 专家指导委员会 译者序 前言 第一部分 建立稳定的系统 第1章 异常处理 第2章 防御性编程 第二部分 标准C++库 第3章 深入理解字符串 第4章 输入输出流 第5章 深入理解模板 第6章 通用算法 第7章 通用容器 第三部分 专题 第8章 运行时类型识别 第9章 多重继承 第10章 设计模式 第11章 并发 附录 附录A 推荐读物 附录B 其他 索引 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值