友元
友元是一种定义在类外部的普通函数或类,但它需要在类体内进行说明,为了与该类的成员函数加以区别,在说明时前面加以关键字friend
类的三种访问权限从:大家均可访问(public)到仅子类可访问(protected)到类外均不可访问(private).这三种访问范围其实是有漏洞的,并没有考虑到一种情况,比如我现在让类内的数据部分公开,即部分函数/类可用,这时类的三种访问权限任何一种都无法满足需求.所以就有了友元,它提供了除类的三种访问权限之外的一种模式,我称它为自定义访问权限.
这样做是当然有弊端的.类的一个重要特征就是封装.将一些数据私有化,使得类外无法直接访问,而友元会破坏这种特征.这可能就是封装与共享的矛盾的一种妥协,一般来说不建议使用.
友元的两种形式
需要说明的是,友元的声明只要在类内即可,与其在什么访问权限下无关.
友元函数
在需要获取权限的类内以**"friend+函数声明"的方式声明友元函数**.
下面的代码展示了如何声明并使用一个友元友元函数.
#include <iostream>
using namespace std;
class Man {
public:
Man(int m = 0):money(m){}
int getMoney() {
return money;
}
friend void changeMoney(Man& man);//友元函数的声明,既可以声明为public,也可为private、protected
private:
int money;
};
void changeMoney(Man& man) {//该函数为Man类的友元函数,可访问并改变Man类内的所有成员
man.money += 100;
}
int main(void) {
Man man(100);
cout << "拥有的钱:" << man.getMoney() << endl;
changeMoney(man);
cout << "改变后钱的数量为:" << man.getMoney() << endl;
system("pause");
return 0;
}
友元类
友元类与友元函数类似,在需要获取权限的类内以**"friend+(友元)类声明"的方式声明友元类**.
下面的代码展示了如何声明并使用一个友元类.
#include <iostream>
using namespace std;
class Man {
public:
Man(int m = 0):money(m){}
int getMoney() {
return money;
}
friend class Woman;//友元类的声明,既可以声明为public,也可为private、protected
private:
int money;
};
class Woman {//该类为Man类的友元类,该类内部的所有方法都可访问并改变Man类内的任意成员
public:
Woman(){}
void useAllMoney(Man& man) {
man.money = 0;
}
};
int main(void) {
Man man(100);
cout << "拥有的钱:" << man.getMoney() << endl;
Woman woman;
woman.useAllMoney(man);
cout << "改变后钱的数量为:" << man.getMoney() << endl;
system("pause");
return 0;
}
运算符重载
C++提供了对基本数据类型(int float double char等)的操作,如"+"、"-"、"*"、"/",而当我们自定义一个结构体或是对象的时候,这些基本的操作却无法使用,这时候我们就要用到运算符重载.由你决定如何运算,此时狭义运算变为广义运算.
运算符重载有两种方式,一种是成员函数重载,一种是友元函数重载.
成员函数重载
下面的代码展示了如何通过成员函数重载运算符"+".
#include <iostream>
using namespace std;
class Man {
public:
Man(int m = 0):money(m){}
int getMoney() {
return money;
}
int operator+(Man& man) {//广义"+",不仅返回被加两对象的money总和,并且改变第一个对象的money值
this->money += man.money;
return this->money;
}
private:
int money;
};
int main(void) {
Man man1(100);
cout << "man1拥有的钱:" << man1.getMoney() << endl;
Man man2(400);
//man1+man2可替换,因为实际上执行man1.operator+(man2);
cout << "man对象在+运算后的结果为:" << man1+man2 << endl;
cout << "man1拥有的钱:" << man1.getMoney() << endl;
cout << "man2拥有的钱:" << man2.getMoney() << endl;
system("pause");
return 0;
}
"+"运算需要两个参数,当采用成员函数重载时,只需传入一个,因为this指针提供了一个参数,并且提供的是第一个参数,所以传进来的参数是第二个参数.这就是为什么在做man1+man2的操作时是man1的成员发生了改变.
友元函数重载
下面的代码展示了如何通过友元函数重载运算符"+".这里的友元函数就不以内联的方式展示,而是分开写,使其更像一个函数.并且多重载一个"<<".(<<也是需要两个参数的)
#include <iostream>
using namespace std;
class Man {
public:
Man(int m = 0):money(m){}
int getMoney() {
return money;
}
friend int operator+(Man& man1, Man& man2);
private:
int money;
friend ostream& operator<<(ostream& os, Man& man);//os:输出流对象
};
int operator+(Man& man1, Man& man2) {
man1.money += man2.money;
return man1.money;
}
ostream& operator<<(ostream& os, Man& man) {//思考将输出流对象作为返回值返回的目的?
os << "拥有的钱的数量:" << man.money << endl;
return os;
}
int main(void) {
Man man1(100);
cout << "man1拥有的钱:" << man1.getMoney() << endl;
Man man2(400);
cout << "man对象在+运算后的结果为:" << man1+man2 << endl;//operator(man1, man2);
//实际上执行:operator<<(operator<<(cout, man1), man2);
cout << man1 << man2;//使用重载后的"<<"
system("pause");
return 0;
}
我在用EasyX图形库在控制台上"放视频"、VC的字符集一文中末尾提到过cout和cin的连写,为什么能连写呢?原因就在返回值上,返回了输出/输入流对象,于是返回值自动做了第一个参数,后面的参数做第二个参数,一起传入,继续调用operator<<或operator>>函数.这就是能连写的秘密.
使用建议
- 一般情况下,单目运算符重载,使用成员函数进行重载更方便(不用写参数)
- 一般情况下,双目运算符重载,使用友元函数更直观(注意一些特殊情况,如有时候a+b可能和b+a的结果不同)
总结
不能被重载的运算符
含义 | 符号 |
---|---|
成员访问 | . |
域运算 | :: |
内存长度运算 | sizeof |
三目运算 | ?:: |
预处理 | # |
可以被重载的运算符
含义 | 符号 |
---|---|
双目运算符 | + - * / % |
关系运算符 | == != < <= > >= |
逻辑运算符 | && |
单目运算符 | + (正号) - (负号) * (指针) & (取地址) ++ – |
位运算 | & |
赋值运算符 | = += -= *= /= %= &= |
内存分配 | new delete new[ ] delete[ ] |
其他 | ( )函数调用 ->成员访问 [ ]下标 ,逗号 |