C++模板类的友员 —— 精准是一种礼仪

1、最简单的友员形式

#include <iostream>
using namespace std;

template <typename T> /* 声明模板类 */
class A
{
	T num;
	friend void show_size(); /* 指定友员 */
};

A<double> a_global1;
A<int> a_global2;
A<char> a_global3;

void show_size()
{
	cout << sizeof(a_global1.num) << endl;
	cout << sizeof(a_global2.num) << endl;
	cout << sizeof(a_global3.num) << endl;
}

int main()
{
	show_size(); /* 输出:8、4、1 */
}

以上代码中show_size作为A的友员,无论是参数还是返回值,都与具体化的A的类型没有关系。
根据测试代码也可以看出,show_size中可以直接接触到具体化的A<double>A<int>A<char>num成员。
所以说,show_size是所有具体化A的友员

但是,由于接口太过简单,所以这种友员函数的用处并不是很大,从上面的测试代码也可以看出,它只能引用一些已经存在的(全局)对象。

2、无约束的模板友员

通常情况下我们使用友元函数机制,目的是为了能够可控地突破private和protect权限限制,自由地从class的外部操作class的成员。因此,通常class的友员函数都会有至少1个参数的类型为该class的类型。比如说,我们可能需要一个这样的友员:

friend void show_size(A<xxx> &a);

综上,为了实现将模板类作为友员函数的参数,可以通过实现一种「无约束的模板友员」来实现:
首先,我们需要像实现一个普通的模板函数一样实现一个友员函数:

template <typename K>
void show_size(K &a)
{
	cout << sizeof(a.num) << endl;
}

我们的意图很明显——我们想用具体化的(比如A<double>)代替类型K。从而可以用sizeof获取到实例anum成员的大小。

其次,也是友员函数的核心,要让Ashow_size授予friend权限:

template <typename T> /* 声明模板类 */
class A
{
	T num;

	template <typename K>
	friend void show_size(K &a); /* 指定友员 */
};

其实,这与第1节中的方式没什么区别,只不过由于这里的show_size是模板函数,故在类中声明该函数为友员时,需要加上模板声明:template <typename K>

完整的代码如下

#include <iostream>
using namespace std;

template <typename T> /* 声明模板类 */
class A
{
	T num;

	template <typename K>
	friend void show_size(K &a); /* 指定友员 */
};

template <typename K>
void show_size(K &a)
{
	cout << sizeof(a.num) << endl;
}

int main()
{
	A<int> aint;
	A<double> adouble;
	A<char> achar;
	show_size(aint);	/* 输出:4 */
	show_size(adouble); /* 输出:8 */
	show_size(achar);   /* 输出:1 */
}

以上的实现方法,本质上来说就是将一个模板函数声明为了友元函数。也没什么特别的。

这种友员的实现方式存在一个问题——友员函数被所有具体化的A共享。
假设有如下全局变量:

A<double> a_global1;
A<int> a_global2;
A<char> a_global3;

show_size的实现可以是这样的:

template <typename K>
void show_size(K &a)
{
	cout << sizeof(a.num) << " "
		 << sizeof(a_global1.num) << " "
		 << sizeof(a_global2.num) << " "
		 << sizeof(a_global3.num) << endl;
}

相对于第1节最普通的友元函数,无约束的友员函数只不过是提供了可以将模板类作为参数传入的解决方案。
即对于任何具体化的A来说,show_size都是它的友员。这种自由有时候可能并不是我们希望的。

另外还有一点需要注意的是,show_size作为一个模板函数,其具体化动作是在调用它的时候进行的,如果代码中指示创建了A对象,没有调用show_size,则编译器不会生成show_size的实际代码。这一点上与下一节即将讨论的有约束友元函数存在一些比较微妙的差别。

3、有约束的友员函数

鉴于前2中有友元函数拥有了太过开放的权限。
现在,我希望让友员函数内部只能接受1种具体化的A。比如说,对于A<double>的友员函数,只能将A<double>的private和protect成员公布给它,A<int>A<char>等具体化的A并不将其当做友员。

通过「有约束」的友元函数可以实现这种约束。

友元函数的定义形式不需要改:

template <typename K>
void show_size(K &a)
{
	cout << sizeof(a.num) << endl;
}

A中授予friend权限的方式需要调整:

template <typename T> /* 声明模板类 */
class A
{
	T num;
	friend void show_size<>(A<T> &a); /* 指定友员 & 模板具体化 */
};

上述代码中,需要注意的是show_size后面的<>,这个是在提示「正在进行模板具体化」。相当于将上面show_size的定义中的类型K替换成了A<T>

完整的代码为:

#include <iostream>
using namespace std;

template <typename K>
void show_size(K &);

template <typename T> /* 声明模板类 */
class A
{
	T num;
	friend void show_size<>(A<T> &a); /* 指定友员 */
};

template <typename K>
void show_size(K &a)
{
	cout << sizeof(a.num) << endl;
}

int main()
{
	A<int> aint;
	A<double> adouble;
	A<char> achar;
	show_size(aint);	/* 输出:4 */
	show_size(achar);   /* 输出:1 */
	show_size(adouble); /* 输出:8 */
}

此时,如果我们补充了3个全局变量:

A<double> a_global1;
A<int> a_global2;
A<char> a_global3;

并尝试在友员函数中使用:

template <typename K>
void show_size(K &a)
{
	cout << sizeof(a.num) << " "
		 << sizeof(a_global1.num) << " "
		 << sizeof(a_global2.num) << " "
		 << sizeof(a_global3.num) << endl;
}

将会报错。正如前面所说的那样,show_sizeA的「有约束」友元函数,在其中除了类型K对应的具体化的A以外,都不认为它是友员函数,自然也就不能接触到它们的private成员。

我们可以再进一步地做个实验。
首先将友元函数改一下:

template <typename K>
void show_size(K &a)
{
	cout << sizeof(a.num) << " "
		 << sizeof(a_global3.num) << endl;
}

注意,其中a_global3的类型为A<char>
然后去掉2个测试case:

int main()
{
	A<int> aint;
	A<double> adouble;
	A<char> achar;
	// show_size(aint);
	show_size(achar); /* 输出:1 1 */
	// show_size(adouble);
}

此时可以正常编译并运行,完整的代码为:

#include <iostream>
using namespace std;

template <typename K>
void show_size(K &);

template <typename T> /* 声明模板类 */
class A
{
	T num;
	friend void show_size<>(A<T> &a); /* 指定友员 & 模板具体化 */
};

A<double> a_global1;
A<int> a_global2;
A<char> a_global3;

template <typename K>
void show_size(K &a)
{
	cout << sizeof(a.num) << " "
		 << sizeof(a_global3.num) << endl;
}

int main()
{
	A<int> aint;
	A<double> adouble;
	A<char> achar;
	// show_size(aint);
	show_size(achar); /* 输出:1 1 */
	// show_size(adouble);
}

注意,此时的模板函数show_size的具体化工作,在A被具体化时,就进行了。也就是说,每当有1个类型的A被具体化时,编译器就会对应地具体化一个show_size,并将二者关联为友员的关系。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值