如果一个类模板包含一个非模板友元,则友元被授权可以访问所有模板实例。如果友元自身是模板,类可以授权给所有友元模板实例,也可以只授权给特定实例
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;
}
|