曲径通幽论坛

 找回密码
 立即注册
搜索
查看: 3399|回复: 0
打印 上一主题 下一主题

模板类与友元

[复制链接]

716

主题

734

帖子

2946

积分

超级版主

Rank: 9Rank: 9Rank: 9

积分
2946
跳转到指定楼层
楼主
发表于 2013-12-24 12:21:16 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
模板类声明也可有友元,分为 3 类:

  • 非模板友元。
  • 约束(bound)模板友元,即友元的类型取决于类被实例化时的类型。
  • 非约束(unbound)模板友元,即友元的所有具体化都是类的每一个具体化的友元。

模板类的非模板友元函数
在一个模板类中将一个常规函数声明为友元:
[C++] 纯文本查看 复制代码
template <class T>

class HasFriend

{

   public:

     friend void counts();    // 对于每一个 HasFriend 实例都适用的友元函数

};

上面的声明使得 counts() 函数成为模板所有实例的友元,比如它可以是 HasFriend<int> 和 HasFriend<string> 的友元。

counts() 作为友元函数,它不是通过对象调用(成员函数通过对象调用)的,而且也没有参数,那它能怎么访问 HasFriend 对象呢?可以有多种途径。它可以访问全局对象;也可以使用全局指针访问非全局对象;还可以创建自己的对象;还可以访问独立于对象的模板类的静态数据成员。

可以为友元函数提供模板类参数,可以写成如下形式吗?
  1. friend void report (HasFriend &);
复制代码
答案是不可以。因为不存在 HasFriend 这样的对象,它只是一个模板;只有特定的具体化,如 HasFriend<short> 才行。因此,要提供模板类参数,必须指明具体化,例如:
  1. template <class T>
  2. class HasFriend
  3. {
  4.     friend void report (HasFriend<T> &);
  5. };
复制代码
如用 int 来代替 T 就有: friend void report (HasFriend<int> &); ,此时带有 HasFriend<int>参数的 report() 将成为 HasFriend<int>类的友元。同样,如果用 double 来替换 T,那么就有了 report() 的一个重载版本。

需要注意的是,report() 本身并不是模板函数,而只是用了一个模板作为参数。这意味着必须为要使用的友元定义显示具体化:
  1. void report (HasFriend<short> &) { ... };
  2. void report (HasFriend<int> &) { ... };
复制代码
考虑下面的程序:
[C++] 纯文本查看 复制代码
// 使用非模板友元的友元类
#include <iostream>
using std::cout;
using std::endl;

template <typename T>
class HasFriend
{
private:
    T item;
    static int ct;
public:
    HasFriend(const T & i) : item(i) {ct++;}
    ~HasFriend()  {ct--; }
    friend void counts();
    friend void reports(HasFriend<T> &); // 模板参数
};

// 每一个具体化都有自己的静态数据成员
template <typename T>
int HasFriend<T>::ct = 0;

void counts()
{
    cout << "int count: " << HasFriend<int>::ct << "; ";
    cout << "double count: " << HasFriend<double>::ct << endl;
}

// HasFriend<int> 类的非模板友元
void reports(HasFriend<int> & hf)
{
    cout <<"HasFriend<int>: " << hf.item << endl;
}

// HasFriend<double> 类的非模板友元
void reports(HasFriend<double> & hf)
{
    cout <<"HasFriend<double>: " << hf.item << endl;
}

int main()
{
    cout << "No objects declared: ";
    counts();
    HasFriend<int> hfi1(10);
    cout << "After hfi1 declared: ";
    counts();
    HasFriend<int> hfi2(20);
    cout << "After hfi2 declared: ";
    counts();
    HasFriend<double> hfdb(10.5);
    cout << "After hfdb declared: ";
    counts(); 
    reports(hfi1);
    reports(hfi2);
    reports(hfdb);
    // std::cin.get();
    return 0; 
}

输出结果:

上面程序中,HasFriend 模板有一个静态成员 ct 。这意味着,这个类的每一个特定的具体化都将有自己的静态成员。count() 方法是所有 HasFriend 具体化的友元,它报告两个特定具体化(HasFriend<int> 和 HasFriend<double>)的 ct 值。该程序还提供两个 report() 函数,它们分别是某个特定 HasFriend 具体化的友元。

模板类的约束模板友元函数
在上面的示例中,友元不是一个模板,不过可以通过适当的修改,使友元函数本身也成为一个模板。具体的说,为约束模板友元做准备,要使类的每一个具体化都获得与友元匹配的具体化

步骤如下:

1. 首先,在类的定义的前面声明每一个模板函数
  1. template <typename T> void counts();
  2. template <typename T> void report(T &);
复制代码
2. 接着,在函数中再次将模板声明为友元。这些语句根据类模板参数的类型声明具体化
  1. template <typename TT>
  2. class HasFriendT
  3. {
  4.     friend void counts<TT>();
  5.     friend void report<>(HasFriendT<TT> &);
  6. };
复制代码
声明中的 <> 指出这是模板具体化。对于 report(),<> 可以为空,因为可从函数参数中推断出模板类型参数为 HasFriendT<TT> ,也可以使用下面的形式:
  1. report <HasFriend<TT> >(HasFriendT<TT> &)
复制代码
但 counts 函数没有参数,因此必须使用模板参数语法(<TT>)来指明其具体化。需要注意的是,TT 是 HasFriendT 类的参数类型。

比如,我们用 int 来替换 TT,那么可生成如下定义:
  1. template <int>
  2. class HasFriendT
  3. {
  4.     friend void counts<int>();
  5.     friend void report<>(HasFriendT<int> &);
  6. };
复制代码
3. 为友元提供模板定义

考虑下面完整的示例程序:
[C++] 纯文本查看 复制代码
// 模板类的模板友元
#include <iostream>
using std::cout;
using std::endl;

// 模板原型
template <typename T> void counts();
template <typename T> void report(T &);

// 模板类
template <typename TT>
class HasFriendT
{
private:
    TT item;
    static int ct;
public:
    HasFriendT(const TT & i) : item(i) {ct++;}
    ~HasFriendT() { ct--; }
    friend void counts<TT>();
    friend void report<>(HasFriendT<TT> &);
};

template <typename T>
int HasFriendT<T>::ct = 0;

// 模板友元函数定义
template <typename T>
void counts()
{
    cout << "template size: " << sizeof(HasFriendT<T>) << "; ";
    cout << "template counts(): " << HasFriendT<T>::ct << endl;
}

template <typename T>
void report(T & hf)
{
    cout << hf.item << endl;
}

int main()
{
    counts<int>();
    HasFriendT<int> hfi1(10);
    HasFriendT<int> hfi2(20);
    HasFriendT<double> hfdb(10.5);
    report(hfi1);  // 生成 report(HasFriendT<int> &)
    report(hfi2);  // 生成 report(HasFriendT<int> &)
    report(hfdb);  // 生成 report(HasFriendT<double> &)
    cout << "counts<int>() output:\n";
    counts<int>();
    cout << "counts<double>() output:\n";
    counts<double>();
    // std::cin.get();
    return 0; 
}

输出结果:

在第 1 个示例程序中只包含了 1 个 counts() 函数,它是所有 HasFriend 类的友元。而在上面的程序中,包含了 2 个 counts() 函数,它们分别是某个被实例化的类类型的友元。因为编译器没法从 count() 函数中推断出所需具体化的函数参数,所以在调用中使用 count<int>() 和 count<double>() 指明具体化。但对于 report() 调用,编译器可以从参数类型推断出要使用的具体化。同样,使用 <> 格式也有同样的效果:
  1. report<HasFriendT<int> >(hfi2);   // 与 report(hfi2); 同样
复制代码
从上面程序的输出可以看到,counts<int> 和 counts<double> 报告了模板的大小不同,这表明每种 T 类型都有自己的友元 counts() 。

模板类的非约束模板友元函数
约束模板友元函数是在类外面声明的模板的具体化,即 int 类具体化将获得 int 函数的具体化,其它类型依此类推。

通过在类内部声明模板,可以创建非约束友元函数,即每个函数都是每个类具体化的友元。对于非约束友元,友元模板类型参数与模板类类型参数是不同的
  1. template <typename T>
  2. class ManyFriend
  3. {
  4.    template <typename C, typename D> friend void show2(C &, D &);
  5. }
复制代码
下面示例代码中使用了非约束友元:
[C++] 纯文本查看 复制代码
//  模板类的非约束模板友元
#include <iostream>
using std::cout;
using std::endl;

template <typename T>
class ManyFriend
{
private:
    T item;
public:
    ManyFriend(const T & i) : item(i) {}
    template <typename C, typename D> friend void show2(C &, D &);
};

template <typename C, typename D> void show2(C & c, D & d)
{
    cout << c.item << ", " << d.item << endl;
}

int main()
{
    ManyFriend<int> hfi1(10);
    ManyFriend<int> hfi2(20);
    ManyFriend<double> hfdb(10.5);
    cout << "hfi1, hfi2: ";
    show2(hfi1, hfi2);
    cout << "hfdb, hfi2: ";
    show2(hfdb, hfi2);
    // std::cin.get();
    return 0;
}

输出结果:

在上面的代码中,函数调用 show2(hfi1, hfi2) 与下面的具体化匹配:
  1. void show2<ManyFriend<int> &, ManyFriend<int> &> (ManyFriend<int> & c, ManyFriend<int> & D);
复制代码
因为它是所有 ManyFriend 具体化的友元,所以能够访问所有具体化的 item 成员,这里它访问了 ManyFriend<int> 对象。

同样,show2(hfd, hfi2) 与下面的具体化匹配:
  1. void show2<ManyFriend<double> &, ManyFriend<int> &> (ManyFriend<double> & c, ManyFriend<int> & d);
复制代码
他也是所有 ManyFriend 具体化成员,并访问了 ManyFriend<int> 和 ManyFriend<double> 对象的 item 成员。

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?立即注册

x
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|手机版|Archiver|曲径通幽 ( 琼ICP备11001422号-1|公安备案:46900502000207 )

GMT+8, 2024-4-30 15:24 , Processed in 0.073568 second(s), 24 queries .

Powered by Discuz! X3.2

© 2001-2013 Comsenz Inc.

快速回复 返回顶部 返回列表