1、友元
友元的目的就是让一个函数或者类访问另一个类中private成员
1.1全局函数做友元
写法:在类外定义一个全局函数,如果这个函数访问到了类中private成员,则需要在这个类中声明这个函数为private类型。
这里需要注意的是全局函数形参的写法,传入的形参可以是Person& p,也可以是Person* p
friend void showAge(Person& p);
void showAge(Person& p) {
cout << p.age << endl;
//引用一个对象,对象调用直接用(.)
}
showAge(p);
friend void showAge(Person* p);
void showAge(Person* p) {
cout << p->age << endl;
//指针的调用方式是->
}
showAge(&p);
//形参是指针,故传入的是地址
1.2、类做友元
基本格式和全局函数做友元差不多,就是在一个类中声明另一个类是friend类型。
#include<bits/stdc++.h>
using namespace std;
class phone{
friend class myphone;
//声明另一个类为友元类型
private:
int price;
public:
phone(int x){
price=x;
}
};
class myphone{
private:
phone a;
public:
myphone(int x):a(x){
}
int getprice(){
return a.price;
}
};
int main(){
int p;
cin>>p;
myphone a(p);
cout<<a.getprice();
return 0;
}
不过与全局函数做友元的区别是,在A类中声明B类是友元,意味着B类可以访问A类中的private成员,就说明B类中肯定包含类型为A的对象(如上面的代码中,myphone类是phone类的友元,myphone类中包含phone类型的对象 a),当然,也可以是A* 类型的对象,此时对B类实例变量进行初始化就需要写成A* p = new A,这样p就是指向一个A类型对象的指针。
1.3、成员函数做友元
基本写法和以上两种相同,不过在声明是友元时,需要额外声明这个函数是哪个类中的函数,防止误认为它是全局函数
2、运算符重载
2.1加法
通过p3=p1+p2,想实现功能:p3.m_A=p1.m_A+p2.m_A,p3.m_B=p1.m_B+p2.m_B
但是编译器不知道怎么实现自定义类型相加。
以前我们实现上述功能是通过自定义函数(可以是成员函数或全局函数)的方式,如下:
现在我们可以将成员函数名称改为operator+,这样调用函数就变成了Person p3 =p1.operator+(p2)
可以简写为Person p3 = p1+p2.
同理将全局函数名称改为operator+,则变成Person p3=operator+(p1,p2)
简写为Person p3 = p1 + p2
#include <iostream>
using namespace std;
class Time {
public:
int hours; // 小时
int minutes; // 分钟
Time() {
hours = 0;
minutes = 0;
}
Time(int h, int m) {
this->hours = h;
this->minutes = m;
}
void show() {
cout << hours << " " << minutes << endl;
}
/*加号运算符重载*/
Time operator+(Time p){
Time temp;
temp.minutes = minutes + p.minutes;
temp.hours = hours + p.hours + temp.minutes/60;
temp.minutes%=60;
return temp;
}
};
int main() {
int h, m;
cin >> h;
cin >> m;
Time t1(h, m);
Time t2(2, 20);
Time t3 = t1 + t2;
//也可以写成:Time t3 = t1.operator+(t2);
t3.show();
return 0;
}
运算符重载也可以发生函数重载,例如可以写一个Person p3 = p1 + 10,这时只需再定义一个函数Person operator+(Person& p1 , int num)。
2.2左移运算符重载
笔者认为这个稍微比较难理解
目的,实现利用cout<<输出自定义类型。
例如Person p3(10,20); cout<< p3;,意为输出p3的所有实例变量
下面先用全局函数来重载左移运算符
#include<iostream>
using namespace std;
class Time{
public:
Time(int h,int m){
hours=h;
mins=m;
}
int hours;
int mins;
};
void operator<<(ostream& out,Time& p){
//cout是输出流对象,它的类型是ostream,
//而且全局只能存在一个输出流对象cout,故必须用引用
//当然,这里cout是形参,起的是一个别名,函数内名字可以随意更改
//例如本例中就将cout改名为out,实际上没有任何区别
out<<p.hours<<endl<<p.mins;
}
int main()
{
Time p(6,59);
cout<<p;
//operator<<(cout,p);
return 0;
}
重载之后调用函数应该是operator<<(cout, p);简写为cout << p(这可类比于加法运算符重载中,
Person p3=operator+(p1,p2)简写为Person p3 = p1 + p2)
然后试着用成员函数实现左移运算符重载:
#include<iostream>
using namespace std;
class Time{
public:
Time(int h,int m){
hours=h;
mins=m;
}
int hours;
int mins;
void operator<<(ostream& out){
out<<hours<<endl<<mins<<endl;
}
};
int main()
{
Time p(6,59);
p.operator<<(cout);
p<<cout;
return 0;
}
可以看到,定义在成员函数中会导致一个严重问题,原本的cout<<p变成了p<<cout,虽然没有语法错误可以运行,但是这和我们标准输出的习惯十分不符。因此我们通常需要避免将左移运算符重载函数写成成员函数。
以上的重载还存在一个问题,即链式编程无法实现。我们知道,cout后面可以同时接多个<<,从而实现链式输出,但是我们上面写的函数没法实现这一功能,因为函数的返回值是void。
这个问题的解决在上一篇文章中实现了,这里我们同样地对重载函数的返回类型做出修改。
#include<iostream>
using namespace std;
class Time{
public:
Time(int h,int m){
hours=h;
mins=m;
}
int hours;
int mins;
};
ostream& operator<<(ostream& out,Time& p){
//cout是输出流对象,它的类型是ostream,
//而且全局只能存在一个输出流对象cout,故必须用引用
//当然,这里cout是形参,起的是一个别名,函数内名字可以随意更改
//例如本例中就将cout改名为out,实际上没有任何区别
out<<p.hours<<endl<<p.mins<<endl;
return out;
}
int main()
{
Time p(6,59);
cout<<p<<p<<p;
return 0;
}
因为具有输出功能的是输出流对象cout,因此返回值是输出流对象cout就能让这个返回值继续输出,从而实现链式编程,故返回值定为ostream&,引用是因为全局只能存在一个输出流对象cout
2.3自增运算符重载
通过重载自增运算符来实现++a,a++的功能。
#include<iostream>
using namespace std;
class Myint{
public:
Myint(int a){
A=a;
}
Myint& operator++(){//实现前缀自增
//要返回引用,不能返回值,否则会拷贝一个对象返回
A++;
return *this;
}
//本来两个函数名字应该完全相同,不过这样编译器无法区别
//因此要在其中一个函数的形参里加上占位参数,用于区分
Myint operator++(int){//实现后缀自增
//实现后缀自增的方法是:先保存原来的数,对这个数自增后,
//返回的是原来的数值。
//后缀自增应该返回一个值,因为如果返回引用,temp是这个
//函数里的局部变量,局部变量会被释放,返回的值就清零了。
Myint temp=*this;
A++;
return temp;
}
int A;
};
//注意这里Myint不能是Myint& p
//但是可以写成const Myint& p或Myint p
//因此C++中产生的临时变量是不可修改的常量
ostream& operator<<(ostream& out,Myint p){
out<<p.A;
return out;
}
int main()
{
Myint a(10);
cout<<a++<<' ';
cout<<a<<' ';
cout<<++a<<' ';
cout<<a;
return 0;
}
总结来说,前缀自增返回的是引用,后缀自增返回的是值,而且需要加上占位参数。