前言:C++中对类对象私有部分的访问是被限制了的,一般来说,类的公有类方法是唯一的访问途径,但是这样的限制会影响某些特定情况下的编程(比如重载操作符时)。在这种情况下,C++提供了另一种形式的访问权限:友元。友元分为:友元函数,友元类,友元成员函数。这里先介绍友元函数,其他两种将放在另一篇博客中介绍。
一:为何需要友元
友元提供了另一种访问类对象私有部分的访问权限,通过让函数成为类的友元,可以赋予该函数与类的成员函数相同的访问权限。而这在某些特殊情况下是很有帮助的,比如在重载操作符时。
在使用成员函数重载操作符时,操作符左边的对象为调用对象,意味着重载的操作符左边必须为用户定义的对象。这意味着用户必须按照:对象,操作符,其他类型数据的顺序使用重载后的操作符,限制了操作符的使用场景。
当然还有另一种选择,即使用非成员函数。非成员函数不通过对象调用,所使用的全部参数都是显式参数。这就允许了使用交换后的顺序使用重载后的操作符。但问题是非成员函数无法直接访问类的私有数据,所以可以直接访问类的私有数据的特殊非成员函数——友元函数,就体现了自身的价值。
二:如何创建友元函数
①将友元函数的原型放在类声明中,并在其前面加上关键字friend;
即:friend className operator op(argument-list);
②在类实现文件中定义友元函数,但需要注意的是因为友元函数不是成员函数,所以不要使用::限定符;同时不要在定义时使用关键字friend;
③虽然友元函数在类声明中声明,并具有和成员函数相同的访问权限,但是其不是成员函数,不能用成员函数操作符(.)来调用;
三:友元函数范例
操作符<<已经被重载过,既表示位左移,又和cout合用表示输出。而在C++中,操作符<<又常常被重载以输出类的信息,接下来将用一个范例,表示友元函数的用法以及操作符<<的重载。
//Complex.h
#ifndef COMPLEX_H_
#define COMPLEX_H_
#include <iostream>
class Complex
{
private:
int real; //实部
int imaginary ; //虚部
public:
Complex(); //默认构造函数
Complex(int re,int im); //显式构造函数
Complex operator*(int n)const;
friend Complex operator*(int n,Complex & co); //声明友元函数
friend void operator<<(std::ostream & os, const Complex & c); //由于cout<<class这个形式使用两个对象,
//第一个对象是ostream对象(cout),故这里使用ostream类引用os
};
#endif
//Complex.cpp
#include <iostream>
#include "Complex.h"
Complex::Complex()
{
real = imaginary = 0;
}
Complex::Complex(int re,int im)
{
real = re;
imaginary = im;
}
Complex Complex::operator*(int n)const
{
Complex c;
c.real = real * n;
c.imaginary = imaginary * n;
return c;
}
//接下来的两个友元函数均不要使用限定符::
Complex operator*(int n,Complex & co)
{
Complex c;
c.real = co.real * n;
c.imaginary = co.imaginary * n;
return c;
}
void operator<<(std::ostream & os, const Complex & c)
{
os<<c.real<<" + "<<c.imaginary<<"i"<<std::endl;
}
//User.cpp
#include <iostream>
#include "Complex.h"
int main(){
using std::cout;
Complex number1(1,2); //使用显式构造函数
cout<<number1; //使用重载后的操作符<<输出
number1 = 4 * number1;//使用友元函数进行乘法
cout<<number1;
number1 = number1 * 4;
cout<<number1;
return 0;
}
运行程序以后,结果正确:
上述代码已经显示了友元函数的定义和使用方法,但是操作符<<的重载却留有一点疑问,那就是cout能不能连续多次输出呢?在User.cpp文件中修改代码:
#include <iostream>
#include "Complex.h"
int main(){
using std::cout;
Complex number1(1,2); //使用显式构造函数
Complex number2; //使用默认构造函数
cout<<number1;
number1 = 4 * number1;
cout<<number1;
number2 = number1 * 4;
cout<<"number1 = "<<number1<<" number2 = "<<number2;
return 0;
}
运行代码,发出报错:
找到报错点,发现问题在:
究其原因,是因为友元函数的返回值类型是void,而下一次操作符<<调用时应该使用的是ostream对象,所以更改原程序:
//Complex.h
#ifndef COMPLEX_H_
#define COMPLEX_H_
#include <iostream>
class Complex
{
private:
int real; //实部
int imaginary ; //虚部
public:
Complex(); //默认构造函数
Complex(int re,int im); //显式构造函数
Complex operator*(int n)const;
friend Complex operator*(int n,Complex & co); //声明友元函数
friend std::ostream & operator<<(std::ostream & os, const Complex & c); //由于cout<<class这个形式使用两个对象,
//第一个对象是ostream对象(cout),故这里使用ostream类引用os
};
#endif
//Complex.cpp
#include <iostream>
#include "Complex.h"
Complex::Complex()
{
real = imaginary = 0;
}
Complex::Complex(int re,int im)
{
real = re;
imaginary = im;
}
Complex Complex::operator*(int n)const
{
Complex c;
c.real = real * n;
c.imaginary = imaginary * n;
return c;
}
Complex operator*(int n,Complex & co)
{
Complex c;
c.real = co.real * n;
c.imaginary = co.imaginary * n;
return c;
}
std::ostream & operator<<(std::ostream & os, const Complex & c)
{
os<<c.real<<" + "<<c.imaginary<<"i"<<std::endl;
return os;
}
//User.cpp
#include <iostream>
#include "Complex.h"
int main(){
using std::cout;
Complex number1(1,2); //使用显式构造函数
Complex number2; //使用默认构造函数
cout<<number1;
number1 = 4 * number1;
cout<<number1;
number2 = number1 * 4;
cout<<"number1 = "<<number1<<"number2 = "<<number2;
return 0;
}
运行程序,结果正确: