4.4 友元
生活中你的家有客厅(Public),有你的卧室(Private)
客厅所有来的客人都可以进去,但是你的卧室是私有的,也就是说只有你能进去
但是呢,你也可以允许你的好闺蜜好基友进去。
在程序里,有些私有属性 也想让类外特殊的一些函数或者类进行访问,就需要用到友元的技术
友元的目的就是让一个函数或者类 访问另一个类中私有成员
友元的关键字为 friend
友元的三种实现
- 全局函数做友元
- 类做友元
- 成员函数做友元
4.4.1 全局函数做友元
#include<iostream>
using namespace std;
#include<string>
//全局函数做友元
class building
{
//goodguy全局函数是building的好朋友,可以访问私有空间
friend void goodguy(building* building);
public:
building()
{
m_sittingroom = "客厅";
m_bedroom = "卧室";
}
public:
string m_sittingroom;
private:
string m_bedroom;
};
//全局函数
void goodguy(building *building)
{
cout << "好基友全局函数正在访问:" << building->m_sittingroom << endl;
cout << "好基友全局函数正在访问:" << building->m_bedroom << endl;
}
void test1()
{
building building;
goodguy(&building);
}
int main()
{
test1();
system("pause");
return 0;
}
4.4.2 类做友元
#include<iostream>
using namespace std;
#include<string>
//类做友元
class building;
class goodguy
{
public:
goodguy();
void visit();//参观函数 访问building中的属性
building* m_building;
};
class building
{
friend class goodguy;//goodguy是本类的好朋友,可以访问私有成员
public:
building();
public:
string m_SittingRoom; //客厅
private:
string m_BedRoom;//卧室
};
building::building()
{
m_SittingRoom = "客厅";
m_BedRoom = "卧室";
}
goodguy::goodguy()
{
//创建一个建筑物对象
m_building = new building;
}
void goodguy::visit()
{
cout << "好基友正在访问" << m_building->m_SittingRoom << endl;
cout << "好基友正在访问" << m_building->m_BedRoom << endl;
}
void test1()
{
goodguy gg;
gg.visit();
}
int main()
{
test1();
system("pause");
return 0;
}
4.4.3 成员函数做友元
#include<iostream>
using namespace std;
#include<string>
//成员函数做友元
class building;
class goodguy
{
public:
goodguy();
void visit();//参观函数 访问building中的私有成员
void visit2();//不能访问私有成员
building* m_building;
};
class building
{
friend void goodguy::visit();//goodguy类下的visit是本类的好朋友,可以访问私有成员
public:
building();
public:
string m_SittingRoom; //客厅
private:
string m_BedRoom;//卧室
};
building::building()//下面都是练习一下类外实现定义
{
m_SittingRoom = "客厅";
m_BedRoom = "卧室";
}
goodguy::goodguy()
{
//创建一个建筑物对象
m_building = new building;//在堆区创建空间,用指针维护
}
void goodguy::visit()
{
cout << "visit正在访问" << m_building->m_SittingRoom << endl;
cout << "visit正在访问" << m_building->m_BedRoom << endl;
}
void goodguy::visit2()
{
cout << "visit2正在访问" << m_building->m_SittingRoom << endl;
//cout << "visit2正在访问" << m_building->m_BedRoom << endl; //没有给friend
}
void test1()
{
goodguy gg;
gg.visit();
gg.visit2();
}
int main()
{
test1();
system("pause");
return 0;
}
4.5 运算符重载
运算符重载概念:对已有的运算符重新进行定义,赋予其另一种功能,以适应不同的数据类型
4.5.1 加号运算符重载
作用:实现两个自定义数据类型相加的运算
#include<iostream>
using namespace std;
//加号运算符重载
class person
{
public:
//1.成员函数重载+号
//person operator+(person& p)
//{
// person temp;
// temp.m_a = this->m_a + p.m_a;
// temp.m_b = this->m_b + p.m_b;
// return temp;
//}
int m_a;
int m_b;
};
//2.全局函数重载+号
person operator+(person& p1, person& p2)
{
person temp;
temp.m_a = p1.m_a + p2.m_a;
temp.m_b = p1.m_b + p2.m_b;
return temp;
}
//函数重载
person operator+(person& p, int a)
{
person temp;
temp.m_a = p.m_a + a;
temp.m_b = p.m_b + a;
return temp;
}
void test1()
{
person p1;
p1.m_a = 10;
p1.m_b = 10;
person p2;
p2.m_a = 10;
p2.m_b = 10;
//成员函数重载本质调用
//person p3 = p1.operator+(p2);
person p3 = p1 + p2;
cout << "p3.m_a= : " << p3.m_a << endl;
cout << "p3.m_b= : " << p3.m_b << endl;
//全局函数重载的本质调用
//person p3 = operator+(p1, p2);
person p4 = p1 + p2;
cout << "p4.m_a= : " << p4.m_a << endl;
cout << "p4.m_b= : " << p4.m_b << endl;
//运算符重载 也可以发生函数重载
person p5 = p1 + 100;//相当于operator+(p1,100)
cout << "p5.m_a= : " << p5.m_a << endl;
cout << "p5.m_b= : " << p5.m_b << endl;
}
int main()
{
test1();
system("pause");
return 0;
}
总结1:对于内置的数据类型的表达式的的运算符是不可能改变的
总结2:不要滥用运算符重载
4.5.2 左移运算符重载
作用:可以输出自定义数据类型
#include<iostream>
using namespace std;
//左移运算符重载
class person
{
friend ostream& operator<<(ostream& cout, person& p);
public:
person(int a,int b)
{
m_a = a;
m_b = b;
}
//利用成员函数重载 左移运算符 p.operator<<(cout) 简化版本p<<cout;
//不会利用成员函数重载<<运算符,因为无法实现cout 在左侧
/*void operator<<(cout)
{
}*/
private:
int m_a;
int m_b;
};
//只能利用全局函数重载左移运算符
ostream& operator<<(ostream &cout, person &p) //本质:operator<<(cout,p) 简化cout<<p;
{
cout << "m_a=: " << p.m_a << " m_b=: " << p.m_b ;
return cout;
}
void test1()
{
person p1(10,10);
cout << p1 << endl;
}
int main()
{
test1();
system("pause");
return 0;
}
总结:重载左移运算符配合友元可以实现输出自定义数据类型
4.5.3 递增运算符重载
作用: 通过重载递增运算符,实现自己的整型数据
//区分前置后置区别
前置operator++()
后置operator++(int)
#include<iostream>
using namespace std;
//递增符重载
class myinteger
{
friend ostream& operator<<(ostream &cout, myinteger myint);
public:
myinteger()
{
m_num = 0;
}
//重载前置++运算符
//返回引用是为一直对一个数据进行递增操作,不然会把加完数据变成一个新的变量重新加
myinteger& operator++()
{
//先做递增操作
m_num++;
//把自身做一个返回
return *this;
}
//重载后置++运算符
//void operator++(int) int代表占位参数,可以区分前置和后置递增
myinteger operator++(int)
{
//先记录当时结果
myinteger temp = *this;
//递增
m_num++;
//将记录结果返回
return temp;
}
private:
int m_num;
};
//重载左移运算符
ostream& operator<<(ostream &cout, myinteger myint)
{
cout << myint.m_num ;
return cout;
}
void test1()
{
myinteger myint;
cout << ++(++myint) << endl;
cout << myint << endl;
}
void test2()
{
myinteger myint;
cout << myint++ << endl;//后置只能加一次,没有引用
cout << myint << endl;
}
int main()
{
test1();
test2();
system("pause");
return 0;
}
总结: 前置递增返回引用,后置递增返回值
4.5.3.1递减运算符重载
#include<iostream>
using namespace std;
class mydecrease
{
friend ostream& operator<<(ostream &cout, mydecrease md);
public:
mydecrease()
{
m_num = 0;
}
mydecrease& operator--()
{
m_num--;
return *this;
}
mydecrease operator--(int)
{
mydecrease temp;
m_num--;
return temp;
}
private:
int m_num;
};
ostream& operator<<(ostream &cout, mydecrease md)
{
cout << md.m_num;
return cout;
}
void test1()
{
mydecrease md;
cout << --md << endl;
cout << md << endl;
}
void test2()
{
mydecrease md;
cout << md-- << endl;
cout << md << endl;
}
int main()
{
test1();
test2();
system("pause");
return 0;
}
4.5.4 赋值运算符重载
c++编译器至少给一个类添加4个函数
- 默认构造函数(无参,函数体为空)
- 默认析构函数(无参,函数体为空)
- 默认拷贝构造函数,对属性进行值拷贝
- 赋值运算符 operator=, 对属性进行值拷贝
如果类中有属性指向堆区,做赋值操作时也会出现深浅拷贝问题
示例:
#include<iostream>
using namespace std;
class person
{
public:
person(int age)
{
m_age = new int(age);//堆区开辟内存
}
~person()
{
if (m_age != NULL)
{
delete m_age;
m_age = NULL;
}
}
//赋值运算符重载
person& operator=(person& p)
{
//编译器的浅拷贝
//m_age=p.m_age;
//应先判断是否有属性在堆区,如果有先释放干净,然后在进行深拷贝
if (m_age != NULL)
{
delete m_age;
m_age = NULL;
}
//深拷贝
m_age = new int(*p.m_age);
return *this;
}
int* m_age;
};
void test1()
{
person p1(18);
person p2(20);
person p3(20);
p3=p2 = p1;//赋值操作
cout << "p1的年龄: " << *p1.m_age << endl;
cout << "p2的年龄: " << *p2.m_age << endl;
cout << "p3的年龄: " << *p3.m_age << endl;
}
int main()
{
test1();
system("pause");
return 0;
}
4.5.5 关系运算符重载
作用: 重载关系运算符,可以让两个自定义类型对象进行对比操作
示例:
#include<iostream>
using namespace std;
#include<string>
//关系运算符重载
class person
{
public:
person(string name, int age)
{
m_name = name;
m_age = age;
}
bool operator==(person &p)
{
if (this->m_name == p.m_name && this->m_age == p.m_age)
{
return true;
}
else
{
return false;
}
}
bool operator!=(person& p)
{
if (this->m_name != p.m_name || this->m_age != p.m_age)
{
return true;
}
else
{
return false;
}
}
string m_name;
int m_age;
};
void test1()
{
person p1("汤姆",19);
person p2("汤姆",29);
if (p1==p2)
{
cout << "相同" << endl;
}
else
{
cout << "不同" << endl;
}
if (p1 != p2)
{
cout << "不同" << endl;
}
else
{
cout << "相同" << endl;
}
}
int main()
{
test1();
system("pause");
return 0;
}
4.5.6 函数调用运算符重载
- 函数调用运算符 () 也可以重载
- 由于重载后使用的方式非常像函数的调用,因此称为仿函数
- 仿函数没有固定写法,非常灵活
示例:
#include<iostream>
using namespace std;
#include<string>
//函数调用运算符重载
//打印输出类
class myprint
{
public:
//重载函数调用运算符
void operator()(string test)
{
cout << test << endl;
}
};
void myprint2(string test)
{
cout << test << endl;
}
void test1()
{
myprint mp;
mp("hello world");//由于使用起来非常像一个函数,所以叫仿函数
myprint2("hhhhhh");
}
//仿函数非常灵活,没有固定写法
//加法类
class myadd
{
public:
int operator()(int num1, int num2)
{
return num1 + num2;
}
};
void test2()
{
myadd ma;
int ret=ma(100, 100);
cout << ret << endl;
//匿名对象
cout << myadd()(100, 100) << endl;//相当于myadd()=ma;看到带()就要反应是匿名对象,然后看它是否使用重载值
}
int main()
{
test1();
test2();
system("pause");
return 0;
}