- 作者: 雪山肥鱼
- 时间:20220101 13:39
- 目的: 类模板中的友元
友元的定义
让类B 成为 类A 的友元。那么类B 可以在自己的成员函数中访问类A的所有成员,而不管这些成员是否用了public private protected 进行修饰。
这里需要注意
类B 成为 类A 的友元,即类B 主动和 类A 成为朋友,是类B 可以访问类A的私有成员,反之则错误。
我们来讨论 类A 和 类B 都成为类模板的情况。
类模板的某个实例成为友元类
即B<long>, 实例化,让B 成为 long 的类型
template <typename U> class B;//前向声明
template<typename T>
class A {
friend class B<long>;//不需要任何的 public private protected 访问修饰符
private:
int data;
};
template<typename U>
class B {
public:
void callAFunc() {
A<int> aObj;
aObj.data = 5;
cout << aObj.data << endl;
}
};
int main() {
B<long> bObj;
bObj.callAFunc();
};
- 这里是需要对B 进行前向声明。
- friend class B<long> 不需要任何的访问限制符进行修饰
- 从 B<long> 改成 B<int> 立刻失效。因为只有 class B<long> 这个实例化了的类,才是 类模板 A 的友元。
全类型 友元
//template <typename U> class B; 可去掉声明, B成为A 的朋友,B 可以访问A
template<typename T>
class A {
//friend class B<long>;
template<typename> friend class B;
private:
int data;
};
template<typename U>
class B {
public:
void callAFunc() {
A<int> aObj;
aObj.data = 5;
cout << aObj.data << endl;
}
};
int main() {
B<int> bObj;
bObj.callAFunc();
};
- 对friend 进行修改。
- 可以省略 模板B 的前向声明
让类型模板参数成为友元类
如果类型模板 用一个自己创建的类类型进行实例化,如何让这个类成为当前类模板的友元类
用文字难以形容,代码实际一些:
template<typename T>
class A {
//让传进来的CF 成为类模板 A 的 友元类(如果传进来的是CF,则实例化成CF类)
friend T;
private:
int data;
};
class CF {
public:
void callAFunc() {
A<CF> aobj1;
aobj1.data = 12; //对 类模板的私有成员进行了访问
cout << aobj1.data << endl;
}
};
int main() {
CF cf;
cf.callAFunc();
/*
//这肯定不行的。
A<CF> aobj1;
aobj1.data = 12;
*/
return 0;
}
但是,依旧存在问题,如果这时候传进来的 是 一个
普通类型,如int,则这个友元就会失效, 即忽略 friend
class CF {
public:
void callAFunc() {
A<CF> aobj1;
aobj1.data = 12;
cout << aobj1.data << endl;
//当然如果传进来的 非 一个 类类型,则会被忽略。
A<int> aobj2;
aobj2.data = 12;//因为CF类并不是A<int> 的友元类,所以不能访问aobj2这个 A<int> 类的私有成员变量
}
};
解决方案:增加一行代码即可
template<typename T>
class A {
friend T;
friend class CF;//不管A 被 具体实例化成 哪个类(int也好,其他类也号),CF类都是 class A 的友元
private:
int data;
};
class CF {
public:
void callAFunc() {
A<CF> aobj1;
aobj1.data = 12;
cout << aobj1.data << endl;
//当然如果传进来的 非 一个 类类型,则会被忽略。
A<int> aobj2;
aobj2.data = 12;//因为CF类并不是A<int> 的友元类,所以不能访问aobj2这个 A<int> 类的私有成员变量
}
};
int main() {
CF cf;
cf.callAFunc();
/*
//这肯定不行的。
A<CF> aobj1;
aobj1.data = 12;
*/
return 0;
}
友元函数
函数模板可以被声明为友元函数
传统模板函数回顾
template<typename U, typename V>
void func(U val1, V val2) {
cout << "val1 = " << val1 << endl;
cout << "val2 = " << val2 << endl;
}
int main() {
//调用func 方法很多
func(2, 3);//编译器自己推断
func<float>(4.6f, 5);//第一个指定,第二个编译器自己推出
func<int, int>(1, 2);// 完全手工指定模板参数
return 0;
}
针对特定类型的 友元函
//只是针对某一特定类型
//增加声明
template<typename T, typename V> void func(T, V);
class Men {
//friend void func<int, int>(int, int);//实际上是已经被实例化模板实参了。
//friend void func<>(int, int);//可以推断出来的,没有问题
//friend void func<int>(int, int);//可以
private:
void funcmen() const
{
cout << "Men::funcmen() is called" << endl;
}
};
template<typename U, typename V>
void func(U val1, V val2) {
Men mymen;
mymen.funcmen();
}
int main() {
//func(2,3); 会实例化出 void func<int,int>(int, int) {...},所以可以在men类中操作
func(2, 3);
return 0;
}
当然把Men 改成 模板 也是可以的
友元模板
//template<typename T, typename V> void func(T, V);
template<typename T>
class Men {
template<typename U, typename T> friend void func(U val1, T val2);
private:
void funcmen() const
{
cout << "Men::funcmen() is called" << endl;
}
};
template<typename U, typename V>
void func(U val1, V val2) {
Men<int> mymen;
mymen.funcmen();
}
int main() {
//func(2,3); 会实例化出 void func<int,int>(int, int) {...},所以可以在men类中操作
func(2, 3);
func<int, double>(2.0, 3.1);
return 0;
可以舍去前向声明。
泛化版本声明为Men类模板的友元函数之后,那么func函数的特化版本也会被看成Men类模板的友元。
template<typename T>
class Men {
template<typename U, typename T> friend void func(U val1, T val2);
private:
void funcmen() const
{
cout << "Men::funcmen() is called" << endl;
}
};
template<typename U, typename V>
void func(U val1, V val2) {
Men<int> mymen;
mymen.funcmen();
}
template<>
void func<int, int>(int val1, int val2) {
cout << "func<>() 全特化被调用" << endl;
Men<int> mymen;
mymen.funcmen();
}
int main() {
func(2, 3);
func<int, double>(2.0, 3.1);
return 0;
}
另注:
编译器会把全特化的func函数模板看待成一个实例化过的函数模板。
在类模板中定义友元函数
//在类模板中定义友元函数
//是定义 非声明,且只有在代码中调用了函数的时候,编译器才会实例化出这个函数
//一般都是因为在该友元函数中 会用到类模板, 当成全局函数看待就行了
template<typename Z>
class Men {
friend void funz(Men<Z> & temp) {
temp.funcmen();
}
private:
void funcmen() const
{
cout << "Men::funcmen() is called" << endl;
}
};
int main() {
Men<double> mymen2;
funz(mymen2);//类似全局函数,直接调用就行了,只有调用的时候,才会被实例化出来
return 0;
}
- 类似全局函数
- 只有当被调用,才会被实例化
- 因为funz在类模板中且函数体比较简单,会被当成内联函数使用
网友评论