上来先贴结论:
类模板类外实现不要滥用友元
友元函数
先来复习一下友元函数。
- 友元函数是可以直接访问类的私有成员的非成员函数。它是定义在类外的普通函数,它不属于任何类,但需要在类的定义中加以声明,声明时只需在友元的名称前加上关键字friend。
问题:
根据友元函数的特性,他是非成员函数;
在模板类中使用友元函数常常会遇到这么一个报错!
无法解析的外部符号
错误代码示例:
#include<iostream>
using namespace std;
template<class T>
class Person {
public:
Person(T age, T id);
friend ostream& operator<<(ostream & os, Person<T> & p);
void show();
private:
T mAge;
T mID;
};
int main()
{
Person<int> p1(1);
p1.show();
return 0;
}
template<class T>
ostream& operator<<(ostream & os, Person<T> & p)
{
cout << "age" << p.mAge << "id" << p.mID << endl;
return os;
}
错误原因:
第一次编译的函数头和第二次编译的函数头不同,
friend ostream& operator<<(ostream & os, Person<T> & p);
ostream& operator<<(ostream & os, Person<T> & p);
模板的机制:模板会有二次编译,第一次编译检查类模板或函数模板是否有词法语法等错误,在使用模板函数,编译器会根据实际类型不同生成不同的函数,然后就能找到相应类型的函数体。在实际运用中,模板函数可以利用编译器自动推断类型,可以显式或隐式使用函数模板,类模板必须显式使用,告诉编译器模板类的类型。
友元函数虽然可以访问类内部的成员,但是它相对于类是独立的,它的实现不能依赖类。代码中用到模板类template 而在类内声明友元函数的时候也用到了,所以此时友元函数是依赖于类的实现而实现的,编译器才会报错。
解决方案:
- 第一种:可以将友元函数的实现放在类的内部
- 第二种:在类的内部声明友元函数的时候在之前为它单独配一个模板类型,然后在外部实现
友元函数的类内实现
可以将友元函数的实现放在类的内部
#include<string>
#include<iostream>
using namespace std;
template<class T>
class Person {
public:
Person(T age, T id)
{
this->mAge = age;
this->mID = id;
}
friend ostream& operator<<(ostream & os, Person<T> & p)
{
cout << "age" << p.mAge << "id" << p.mID << endl;
return os;
}
void show()
{
cout << "age" << mAge << "id" << mID << endl;
}
private:
T mAge;
T mID;
};
void test01()
{
Person<int> p(10, 20);
p.show();
//cout << p;
}
int main()
{
test01();
return 0;
}
友元函数的类外实现
在类的内部声明友元函数的时候在之前为它单独配一个模板类型,然后在外部实现
#include<string>
#include<iostream>
using namespace std;
//这里实际上就是先告诉编译器这俩模板不是一回事,友元函数有自己的模板
template<class T>class Person;
template<class T>void print(Person<T>& p);
template<class T>
class Person {
public:
Person(T age, T id);
friend void print<T>(Person<T>& p);
void show();
private:
T mAge;
T mID;
};
template<class T>
Person<T>::Person(T age, T id)
{
this->mAge = age;
this->mID = id;
}
template<class T>
void Person<T>::show()
{
cout << "age" << mAge << "id" << mID << endl;
}
template<class T>
void print(Person<T>& p)
{
cout << "print" << p.mAge << p.mID << endl;
}
void test01()
{
Person<int> p(10, 20);
//p.show();
//cout << p;
print(p);
}
int main()
{
test01();
return 0;
}
综上
综上所诉,在模板类中使用友元函数很容易出问题,并且不会直接报错位置,不容易排查,所以不要滥用!