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;
}
时间: 2024-10-06 01:19:27

C++中模板类的友元重载的相关文章

模板类的友元重载

模板类的友元重载和普通类的友元重载有不同之处,可以参考这篇CSDN博客http://blog.csdn.net/ozwarld/article/details/7770808 #include <iostream> using namespace std; template <class T> class Test; // 模板类前置声明 template<class T> ostream& operator << (ostream& out

C++中模板类使用友元模板函数

在类模板中可以出现三种友元声明:(1)普通非模板类或函数的友元声明,将友元关系授予明确指定的类或函数.(2)类模板或函数模板的友元声明,授予对友元所有实例的访问权.(3)只授予对类模板或函数模板的特定实例的访问权的友元声明. (1)普通友元: template<class T> class A{ friend void fun(); //... };此例中fun可访问A任意类实例中的私有和保护成员 (2)一般模板友元关系 template<class type> class A{

在c++ 模板类外写 操作符重载函数,并且是模板类的友元函数

看视频教程说不能在c++ 模板类外写 操作符重载函数,并且是模板类的友元函数 我试了试,可以,放出测试代码: #include <iostream> using namespace std; template<typename T> class A { public: A(T a) { this->a = a; } template<typename T> //加上这句就可以了 friend A<T> operator+(A<T> &

模板类的友元函数

非模板友元函数 模板类的非模板友元函数是说该模板类的友元函数只是一个普通函数,并且该函数是非模板函数或该函数不视为模板函数.这里包含了两种情况,下面分别就两个例子进行说明. • 函数是非模板函数 这一类友元函数特点是不带有参数列表,例如:friend void Fn().这类友元函数通常可以用于全局对象的访问. #include <iostream> using namespace std; template <class T> class MyNumber { private:

模板类与运算符重载

我自定义了一个模板类并重载了运算符,预定义实现功能为能实现对数组一般操作,类似于vector. #ifndef ARRAY_H #define ARRAY_H #include <iostream> using namespace std; template<class T> class Array{ friend ostream& operator<<(ostream&, const Array &); friend istream&

C++学习笔记(一)模板类的友元模板函数Boolan

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 #include<iostream> #include<string> using namespace std; template<class T> class Test; template<class T> ostream& operator<&l

C++ 中模板类的模板成员函数在类外定义

因为很多书都没讲到这种情况, 曾经有这个问题但一直没答案,所以我一直以为模板类的模板成员函数只能在类内定义,直到我在某个开源代码里看到原来要加两个 template<>  ............ (T_T) template<typename T1> class MyObjectT { public: template<typename T2> void Foo(T2 v); }; template<typename T1> template<typ

模板类和友元

非模板友元 template<typename T> class HasFriend { public: friend void report(HasFriend<T>&); }; // the following code is necessary void report(HasFriend<int>& hf) { //details } void report(HasFriend<double>& hf) { //details

类模板,多种类型的类模板,自定义类模板,类模板的默认类型,数组的模板实现,友元和类模板,友元函数,类模板与静态变量,类模板与普通类之间互相继承,类模板作为模板参数,类嵌套,类模板嵌套,类包装器

 1.第一个最简单的类模板案例 #include "mainwindow.h" #include <QApplication> #include <QPushButton> #include <QLabel> template<class T> class run { public: T w; void show() { w.show(); } void settext() { w.setText("A"); }