一.友元
类的数据成员一般定义为私有成员,仅能通过类的成员函数才能读写。如果数据成员定义成公共的,则又破坏了封装性。在某些情况下需要频繁调用数据成员,但是参数传递类型检查都需要时间消耗,所以设计了友元,友元不是类的成员,但是却可以调用类的成员变量.
1.友元函数
友元函数是一种定义在类外部的普通函数,但是需要在类中进行声明。为了和该类的普通成员函数加以区别,在声明前面加了一个关键字friend。友元函数不是成员函数,但是他能够访问类中的私有成员。
注意事项
- 友元函数没有this指针,因为友元函数不是成员函数
- 友元函数的“声明”可以放置到类中的任何位置,不受权限修饰符的影响。
- 一个友元可以访问多个类,只需要在各个类中分别“声明”。
2.友元类
当一个类B成为了另一个类Test的朋友时,类Test的所有成员都能被类B访问,此时类B就是类Test的友元类。
注意事项:
- 友元关系不能被继承
- 友元关系不具有交换性(比如:类B声明成类Test的友元,类B可以访问类Test所有的成员,但是类Test不能访问类B中的私有成员,如果需要访问,需要将类Test声明成类B的友元,即互为友元
3.友元成员函数
使类B中的成员函数成为类Test的友元成员函数,这样类B的该成员函数就可以访问类Test的所有成员了。
友元成员函数的声明格式
friend 数据类型 类名::成员函数名(参数);
二.运算符重载
可以将运算符看做是函数处理,比如+实际上就是add函数,而a+b中的a,b就是参数,返回值就是和.
所以函数可以重载,运算符也可以重载,但是存在不能重载的运算符 . (成员运算符) *(指针运算符) ? :(三目运算符) sizeof ::(作用域)
1.友元函数运算符重载
#include <iostream>
using namespace std;
class MyInt
{
private:
int a;
public:
MyInt(int a):a(a){}
int get_int()
{
return a;
}
// + 运算符重载
friend MyInt operator +(MyInt &i,MyInt &i2);
};
// 友元函数 实现
MyInt operator +(MyInt &i,MyInt &i2)
{
// int → MyInt 触发构造函数隐式调用
return i.a + i2.a;
}
int main()
{
MyInt int1(2);
MyInt int2(int1); // 拷贝构造函数
MyInt int3 = int1 + int2;
cout << int3.get_int() << endl; // 4
return 0;
}
2.成员函数运算符重载
成员函数运算符重载相比于友元函数重载,最主要的区别在于,友元函数的第一个输出参数,在成员函数运算符重载中使用this指针代替。因此相同的运算符重载,成员函数运算符重载比友元函数运算符重载参数少一个。
#include <iostream>
using namespace std;
class MyInt
{
private:
int a;
public:
MyInt(int a):a(a){}
int get_int()
{
return a;
}
MyInt operator +(MyInt &i2);
MyInt operator ++();
MyInt operator ++(int);
};
// 成员函数 类外实现
MyInt MyInt::operator +(MyInt &i2)
{
// int → MyInt 触发构造函数隐式调用
return this->a + i2.a;
}
// 前置自增
MyInt MyInt::operator ++()
{
return ++this->a;
}
// 后置自增
MyInt MyInt::operator ++(int)
{
return this->a++;
}
int main()
{
MyInt int1(2);
MyInt int2(int1); // 拷贝构造函数
MyInt int3 = int1 + int2;
cout << (++int3).get_int() << endl;
cout << int3.get_int() << endl;
return 0;
}
3.特殊运算符重载
赋值运算符重载
如果不写,编译器会自动添加一个赋值运算符重载函数,
#include <iostream>
using namespace std;
class MyInt
{
private:
int a;
public:
MyInt(int a):a(a){}
int get_int()
{
return a;
}
// 编译器会自动添加赋值运算符重载函数
MyInt & operator =(MyInt &i)
{
cout << "赋值运算符被调用了" << endl; // 编译器自动添加的赋值运算符重载函数不会打印这句话
this->a = i.a;
return *this;
}
};
int main()
{
MyInt int1(2);
MyInt int4(3);
cout << int4.get_int() << endl;
int4 = int1; // 赋值运算符重载
cout << int4.get_int() << endl;
return 0;
}
编译器会自动添加哪些函数?
无参构造函数,拷贝构造函数,析构函数,赋值运算符重载函数
类型转换运算符重载
必须使用成员函数运算符重载,且格式特殊
#include <iostream>
using namespace std;
class MyInt
{
private:
int a;
string str = "hello";
public:
MyInt(int a):a(a){}
int get_int()
{
return a;
}
// 编译器会自动添加赋值运算符重载函数
MyInt & operator =(MyInt &i)
{
cout << "赋值运算符被调用了" << endl; // 编译器自动添加的赋值运算符重载函数不会打印这句话
this->a = i.a;
return *this;
}
// 类型转换运算符重载
operator int()
{
return a;
}
operator string()
{
return str;
}
};
int main()
{
MyInt int1(2);
int a = int1;
string str = int1;
cout << a << endl;
cout << str << endl;
return 0;
}
4.运算符重载注意事项
- 重载的运算符限制在C++语言中已有的运算符范围,不能创建新的运算符。
- 运算符重载的本质也是函数重载,但是不支持函数参数默认值设定。
- 重载之后的运算符不能改变运算符的优先级和结合性,也不能改变运算符的操作数和语法结构。
- 运算符重载必须基于或包含自定义类型,即不能改变基本数据类型的运算符规则。
- 重载功能应该与原有功能类似,避免没有目的的滥用运算符重载。
- 一般情况下,双目运算符建议使用友元函数进行重载,单目运算符建议使用成员函数进行重载。