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 >修饰,要么有需要有<>两者必须有其一

#include <iostream>

using namespace std;

template<class T> class Test{

	private:
		int num;
	public:
		Test(int n=0){num=n;}

		Test(const Test <T> &copy){num=copy.num;}

		//注意在“<<”后加上“<>”表示这是一个函数模板
		template<class TT> friend ostream& operator<< (ostream & out,const Test<TT> &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;
}




  • 8
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值