C++中模板类的友元重载

一个由《程序员面试宝典》引出的问题。

描述模板类的友元重载,用C++代码实现?

这实际上考察的是下面几个问题:

1.模板类的编写

2.模板类中友元函数的编写

3.什么时候会用到友元重载?答案是各种C++中的运算符。最典型的就是输出操作符<<了。

书上给出的答案如下:

#include <iostream>using namespace std;template<class T> class Test;template<class T> ostream & operator<<(ostream & out,const Test<T> &obj);template<class T> class Test{private:int num;public:Test(int n=0){num=n;}Test(const Test <T> &copy){num=copy.num;}//注意在“<<”后加上“<>”表示这是一个函数模板friend ostream& operator<< <> (ostream & out,const Test<T> &obj);};template<class T> ostream& operator<<(ostream & out,const Test<T> &obj){out<<obj.num;return out;}int main(){Test<int> t(2);cout<<t;return 0;}只是对上面注释的哪一行不是很理解于是翻了下《C++ Primer》,发现书上对这个问题讲的很详细了。复制过来:

类模板中的友元声明

在类模板中可以出现三种友元声明,每一种都声明了与一个或多个实体的友元关系:

(1)普通非模板或函数的友元声明,将友元关系授予明确指定的类或函数

(2)类模板或函数模板的友元声明,授予对有缘所有实例的访问权

(2)只授予对类模板或函数模板的特定实例的访问权的友元声明

1.普通友元

非模板类或非模板函数可以是类模板的友元:

template<class Type> class Bar{//授权给普通类和或函数friend class FolBar;friend void fcn();};这个声明是说,FolBar的成员和fcn函数可以访问Bar类的任何实例的privete成员和protected成员。

2.一般模板友元

友元可以是类模板或函数模板:

template<class Type> class Bar{//授权给Foo1或temp1_fcn1的任何实例template<class T> friend class Foo1;template<class T> friend void temp1_fcn1(const T&);};这些友元声明使用与类本身不同的类型形参,该类型形参指的是Foo1和temp1_fcn1的类型形参。在这两种情况下,都将没有数目限制的类和函数设为Bar的友元。Foo1的友元声明是说,Foo1的任何实例都可以访问Bar的任何实例的私有成员,,类似地,temp1_fcn1的任何实例可以访问Bar的任意实例。

这个友元声明在Bar与其友元Foo1和temp1_fcn1的每个实例之间建立了一对多的映射。对Bar的每个实例而言,Foo1或temp1_fcn1的所有实例都是友元。

3.特定的模板友元关系

除了将一个模板的所有实例设为友元,类也可以只授予对特定实例的访问权。

template<class T> class Foo2;template<class T> void temp1_fcn2(const T&);template<class Type> class Bar{//只授权给参数类型为char*的实例friend class Foo2<char *>;friend void temp1_fcn2<char *>(char * const &);};即使Foo2本身是类模板,友元关系也只扩展到Foo2的形参类型为char*的特定实例。类似地,temp1_fcn2的友元声明是说,只有参数类型为char*的函数实例是Bar类的友元。形参类型为char*的Foo2和temp1_fcn2的特定实例可以访问Bar的每个实例。下面形式的友元声明更加常见:template<class T> class Foo3;template<class T> void temp1_fcn3(const T&);template<class Type> class Bar{//Bar的每一个实例只能访问参数类型和Bar相同的Foo3和temp1_fcn3的实例friend class Foo3<Type>;friend void temp1_fcn3<Type>(const Type &);};这些友元定义了Bar的特定实例与使用同一模板实参的Foo3或temp1_fcn3的实例之间的友元关系,每个Bar实例有一个相关的Foo3和temp1_fcn3友元:

Bar<int> b1;//它的友元是Foo3<int>和temp1_fcn3<int>Bar<string> bs;//它的友元是Foo3<string>和temp1_fcn3<string>只有与给定Bar实例有相同模板实参的那些Foo3或temp1_fcn3版本是友元。因此,Foo3<int>可以访问Bar<int>的私有部分,但不能访问Bar<string>或者任意其他Bar实例的私有部分。

4.声明依赖性

当授予给定模板的所有实例的访问权的时候,在作用域中不需要存在该类模板或函数模板的声明。实质上,编译器将友元声明也当做类或函数的声明对待。

想要限制对特定实例化的友元关系时,必须在可以用于友元声明之前声明类或函数:

template <class T> class A;template <class T> class B{public:friend class A<T>;//ok,A做了声明friend class C;//ok,C是一个普通类template<class S> friend class D;//ok,D是一个模板,并且是对D的所有实例授权friend class E<T>;//error,需要声明friend class F<int>;//error,需要声明};在g++中会出现这个错误:

如果没有事先告诉编译器该友元是一个模板,则编译器将认为该友元是一个普通非模板类或非模板函数。

上面就是《C++ Primer》关于模板类中友元的说明,理解完这些后再来看这道题就很好理解。根据第3点,特定的模板友元关系可以明白为什么需要<>了,实际上是<T>,不过可以只写成<>。

根据第4点类型依赖性可以明白为什么在最上面需要加上声明了。

然后可以按照第2点一般模板友元关系的方式进行改写,代码如下:但是感觉没有这个必要。

注意,此时前面的声明不需要了,因为按照上面第4点,对所有实例都访问权时是不需要事先声明的。

<<后面也不需要<>了。因为此时前面加上了template<class TT>这个关键字。

注意:为了防止以后在类模板或函数模板中漏掉<>这个符号,可以这样记忆,对于类模板或函数模板,要么有template<class >修饰,要么有需要有<>两者必须有其一

于是渐渐开始有些伤怀。

C++中模板类的友元重载

相关文章:

你感兴趣的文章:

标签云: