根据重载操作符那一章的规定,输入输出操作符必须作为友元而不是类的成员函数重载。
在类的模板中重载输入输出操作符的方式主要有两种:
- 在类模板中做friend声明,在模板外做函数定义;
- 在类模板中定义友元操作符;
1,在类外定义友元操作符
1.1 将函数模板设为友元
#include <iostream>
#include <bitset>
using namespace std;
template<int h, int w> class MyScreen
{
public:
MyScreen() :contents(h*w, '#'), width(w), height(h) {}
MyScreen(string new_contents) :contents(new_contents), width(w), height(h) {}
template<int h1, int w1> friend ostream& operator<< (ostream& os, const MyScreen<h1, w1>& ScreenOne);
private:
int width;
int height;
string contents;
};
template<int h2, int w2> ostream& operator <<(ostream &os, const MyScreen<h2, w2> &ScreenOne)
{
os << "height:" << ScreenOne.height << "\n"
<< "width:" << ScreenOne.width << "\n"
<< "contents:" << ScreenOne.contents ;
return os;
}
int main()
{
MyScreen<3, 4> ScreenOne("HuangYang Love WHY");
cout << ScreenOne << endl;
system("pause");
return 0;
}
执行结果:
1.2 将函数模板的一个实例设为友元
其他不变,将类模板的定义改为如下形式:
template<int h, int w> class MyScreen
{
public:
MyScreen() :contents(h*w, '#'), width(w), height(h) {}
MyScreen(string new_contents) :contents(new_contents), width(w), height(h) {}
friend ostream& operator<< <h,w> (ostream& os, const MyScreen<h, w>& ScreenOne);
private:
int width;
int height;
string contents;
};
注意,做友元声明的时候,必须跟上模板形参。
因为这里将输出流操作符定义成了一个函数模板,设为友元的仅仅是这个模板的一个实例。
模板形参不可省略。如果将友元声明改成:
friend ostream& operator<< (ostream& os, const MyScreen<h, w>& ScreenOne);
则编译报错。
“fatal error LNK1120: 1 个无法解析的外部命令”
因为此时,在模板外定义重定义的输出流操作符与模板类声明的友元输出流操作符不是同一个操作符。
代码总结如下:
#include <iostream>
#include <bitset>
using namespace std;
template<int h, int w> class MyScreen
{
public:
MyScreen() :contents(h*w, '#'), width(w), height(h) {}
MyScreen(string new_contents) :contents(new_contents), width(w), height(h) {}
// 整个模板设为友元,OK
template<int h1, int w1> friend ostream& operator<< (ostream& os, const MyScreen<h1, w1>& ScreenOne);
// 模板的一个实例设为友元,OK
//friend ostream& operator<< <h,w> (ostream& os, const MyScreen<h, w>& ScreenOne);
// Error!
//friend ostream& operator<< (ostream& os, const MyScreen<h, w>& ScreenOne);// Error
private:
int width;
int height;
string contents;
};
template<int h2, int w2> ostream& operator <<(ostream &os, const MyScreen<h2, w2> &ScreenOne)
{
os << "height:" << ScreenOne.height << "\n"
<< "width:" << ScreenOne.width << "\n"
<< "contents:" << ScreenOne.contents ;
return os;
}
int main()
{
MyScreen<3, 4> ScreenOne("HuangYang Love WHY");
cout << ScreenOne << endl;
system("pause");
return 0;
}
2,在类内定义友元操作符
template<int h, int w> class MyScreen
{
public:
MyScreen() :contents(h*w, '#'), width(w), height(h) {}
MyScreen(string new_contents) :contents(new_contents), width(w), height(h) {}
//template<int h1, int w1> friend ostream& operator<< (ostream& os, const MyScreen<h1, w1>& ScreenOne)//OK
//friend ostream& operator<< <h, w> (ostream& os, const MyScreen<h, w>& ScreenOne)//OK
friend ostream& operator<< (ostream& os, const MyScreen<h, w>& ScreenOne)//OK
{
os << "height:" << ScreenOne.height << "\n"
<< "width:" << ScreenOne.width << "\n"
<< "contents:" << ScreenOne.contents;
return os;
}
private:
int width;
int height;
string contents;
};
int main()
{
MyScreen<3, 4> ScreenOne("HuangYang Love WHY");
cout << ScreenOne << endl;
system("pause");
return 0;
}
当在类内部定义时,三种声明方式都可以。
3,多文件编译
以上的描述都是针对单文件编译的情况。但是,我们在日常中经常遇到的情况却是多文件编译。
一旦将输出流定义在另外一个文件中时,就会报错。
比如将1.1的例子分成3个文件friend.h、friend.cpp、main.cpp来实现:
friend.h:
#pragma once
#include <iostream>
#include <bitset>
using namespace std;
template<int h, int w> class MyScreen
{
public:
MyScreen() :contents(h*w, '#'), width(w), height(h) {}
MyScreen(string new_contents) :contents(new_contents), width(w), height(h) {}
// 整个模板设为友元
template<int h1, int w1> friend ostream& operator<< (ostream& os, const MyScreen<h1, w1>& ScreenOne);
private:
int width;
int height;
string contents;
};
#include "friend.h"
template<int h2, int w2> ostream& operator <<(ostream &os, const MyScreen<h2, w2> &ScreenOne)
{
os << "height:" << ScreenOne.height << "\n"
<< "width:" << ScreenOne.width << "\n"
<< "contents:" << ScreenOne.contents;
return os;
}
main.cpp:
#include "friend.h"
int main()
{
MyScreen<3, 4> ScreenOne("HuangYang Love WHY");
cout << ScreenOne << endl;
system("pause");
return 0;
}
则编译报错:
“fatal error LNK1120: 1 个无法解析的外部命令”
原因没想明白!!!