主页
文章
分类
系列
标签
简历
【模板与泛型编程03】类模板和友元
发布于: 2022-6-22   更新于: 2022-6-22   收录于: Cpp
文章字数: 558   阅读时间: 3 分钟   阅读量:

如果一个类模板包含一个非模板友元,则友元被授权可以访问所有模板实例。如果友元自身是模板,类可以授权给所有友元模板实例,也可以只授权给特定实例

1、友元类

传统友元类的概念是:让某个类B成为另外一个类A的友元类,这样的话,类B就可以在其成员函数中访问类A的所有成员(成员变量、成员函数),而不管这些成员在类A中是用什么修饰符(private、protected、public)修饰的。

1.1 让类模板的某个实例称为友元类

 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
template <typename U> class B;
template <typename T>
class A
{
	friend class B<long>;
private:
	int data;
};

template <typename U>
class B
{
public:
	void callBAF()
	{
		A<int> atmpobj;
		atmpobj.data = 5 ;
		std::cout << atmpobj.data<<std::endl;
	}
};

int main()
{
	B<long> bobj ;
	bobj.callBAF();
}

让类模板B的特定类模板实例(也就是B<long>)成为类模板A的友元类。也就是说,B<long>实际上代表的是一个具体的类

1.2 让类模板成为友元类模板

整个类模板B变成类模板A的友元类模板

 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
template <typename U> class B;
template <typename T>
class A
{
	template<typename > friend class B;
private:
	int data;
};

template <typename U>
class B
{
public:
	void callBAF()
	{
		A<int> atmpobj;
		atmpobj.data = 5 ;
		std::cout << atmpobj.data<<std::endl;
	}
};
int main()
{
	B<long> bobj ;
	bobj.callBAF();

	B<int> bobjs ;
	bobjs.callBAF();
}

1.3 让类型模板参数成为友元类(C++11)

如果传递进来的类型模板参数是一个类类型,则这个类类型可以成为当前类模板的友元类

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
template <typename T>
class A2
{
	friend T;
private 
	int data;
};

class CF
{
public:
	void callCFAF()
	{
		A2<CF> aobj;
		aobj.data=12;
	}
};

int main()
{
	CF mycfobj;
	mycfobj.callCFAF();
}

类模板有其特殊性,如果传递给类模板A2的类型模板参数不是一个类类型,那么代码行friend T;就会被忽略,从而不能访问data成员变量

2、友元函数

2.1 让函数模板的某个实例成为友元函数

 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
template<typename U , typename V>
void func(U val1 , V val2);
class Men
{
	private: 
		void funcmen()const
		{
			cout<<"Men::funcmen被调用了"<<endl;
		}
	friend void func<int , int>(int , int);
	friend void func<>(float , int);//尖括号内保持为空,编译器会用指定的实参类型推导出模板参数的类型
	friend void func<>(int , float);
};

template<typename U , typename V>
void func(U val1 , V val2)
{
	Men mymen;
	mymen.funcmen();
}
int main()
{
	func(2,3);//func<int ,int>
	func<float>(4.6f , 5);//func<float,int>
	func<int , float>(4,5.6f);//func<int ,float>
}

2.2 友元模板

上面的范例中,只是让函数模板func()的某个实例成为Men类模板的友元函数,而且为了应对不同的情况,还要分别设置多个func()函数模板的实例成为Men类模板的友元函数。显然,如果希望所有func()实例都成为Men类模板的友元函数,像上面这样一个个设置很辛苦。所以,为了做到这点,就需要让函数模板func()成为Men类模板的友元模板。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
class Men
{
	private: 
		void funcmen()const
		{
			cout<<"Men::funcmen被调用了"<<endl;
		}
	template <typename U , typename V> friend void func(U val1 , V val2);
};

template<typename U , typename V>
void func(U val1 , V val2)
{
	Men mymen;
	mymen.funcmen();
}
int main()
{
	func(2,3);//func<int ,int>
	func<float>(4.6f , 5);//func<float,int>
	func<int , float>(4,5.6f);//func<int ,float>
}

所有func()函数模板的实例就都成为Men类模板的友元函数,也不再需要在类模板Men定义的前面增加针对函数模板func()的声明了。编译器是把全特化的func()函数模板看作一个实例化过的函数模板(理解得更直接一点,就是看作一个已经存在了的函数)

2.3 在类模板中定义友元函数

可以在一个类模板中定义(不是声明)一个友元函数,这种友元函数是能被调用的,而且也只有在代码中调用了这个函数时,编译器才会实例化这个函数

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
template <typename Z> 
class Men
{
public:
	friend void func2(Men<Z>&tmpmen){
		tmpmen.funcmen();
	}

	void funcmen()const
	{
		cout<<"Men::funcmen被调用了"<<endl;
	}
};
int main()
{
	Men<double> mymen2;
	func2(mymen2);
}

虽然func2()友元函数被写在一个类模板中,但是类模板被实例化时并不会实例化这些友元函数,只有代码中调用到这些函数时,这些函数才会被实例化。另外值得一提的就是,因为func2()是写在类模板定义中的,所以,当调用func2()时,如果func2()中的代码非常简单,那么聪明的编译器就会把func2()当作一个内联(Inline)函数来处理;如果func2()中的代码比较复杂(如出现了for循环等),那么func2()就很可能不会被当作一个内联函数处理

 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
37
38
39
//由cppinsights.io 生成
template<typename Z>
class Men
{
  public: 
  friend inline void func2(Men<Z> & tmpmen)
  {
    tmpmen.funcmen();
  }
  inline void funcmen() const
  {
    std::operator<<(std::cout, "Men::funcmen\350\242\253\350\260\203\347\224\250\344\272\206").operator<<(std::endl);
  }
};

/* First instantiated from: insights.cpp:20 */
#ifdef INSIGHTS_USE_TEMPLATE
template<>
class Men<double>
{
  public: 
  friend inline void func2(Men<double> & tmpmen)
  {
    tmpmen.funcmen();
  }
  inline void funcmen() const
  {
    std::operator<<(std::cout, "Men::funcmen\350\242\253\350\260\203\347\224\250\344\272\206").operator<<(std::endl);
  }
  // inline constexpr Men() noexcept = default;
};
#endif

int main()
{
  Men<double> mymen2 = Men<double>();
  func2(mymen2);
  return 0;
}