四.运算符重载
1.友元(熟悉)
类的封装,一般成员属性设为private,提高安全性.如果需要访问,需要有public接口。
但有时,需要频繁的访问成员属性。需要调成员函数就需要传参,而参数传递,和类型检查和安全性检查会会影响程序运行的效率
友元是定义在类外部的普通函数。但是需要在类中进行说明。声明时需要用friend关键字;
学习友元的目的就是为了后面的运算符重载,其他时候不要用,会破坏封装
1.1友元函数(重点)
- 定义在类外,不是类内函数,也就没有this指针。就需要对象的引用作为参数,来访问对象中的成员
- 不是类内函数,友元函数的说明可以写在类中的任意位置。不受类中的权限控制。
#include <iostream>
using namespace std;
class Test{
private:
int a;
public:
Test(int a)
{
this->a=a;
}
void show(){
cout<<a<<endl;
}
friend void method(Test & t); //声明
};
//在类外
void method(Test & t){
cout<<t.a<<endl;
}
int main()
{
Test t1(2);
t1.show();
method(t1);
}
1.2 友元类(熟悉)
如果类B是类A的友元类,类B就可以访问类A的所有成员
- 友元关系单向性,不具有交换性
- 友元关系不能传递 A和B B和C 但是A和C不是友元关系
- 友元关系不能继承 父亲的朋友 不是儿子的朋友
#include <iostream>
using namespace std;
class A{
private:
int num=2;
friend class B; //说明B是A的朋友 可以访问A中的任何成员
};
class B{
public:
void func(A &a)
{
cout<<a.num<<endl;
}
};
int main(){
//A把B当朋友 B没把A当朋友
A a;
B b;
b.func(a);
}
2.运算符重载概念
可以被重载的运算符:算术运算符:+、-、*、/、%、++、--
位操作运算符:&、|、~、^(位异或)、<<(左移)、>>(右移)
逻辑运算符:!、&&、||
比较运算符:<、>、>=、<=、==、!=
赋值运算符:=、+=、-=、*=、/=、%=、&=、|=、^=、<<=、>>=
其他运算符:[]、()、->、,、new、delete、new[]、delete[]
不被重载的运算符:
成员运算符 .(点)、指针运算符 *、三目运算符 ? :、sizeof、作用域 ::
C++的运算符重载。默认只支持基本的数据类型。但是对自定义类类型不能直接使用,
这时就需要运算符重载
Integer operator + (const Integer& i1,const Integer& i2)
Integer:返回值
operator +:函数名
3.友元函数运算符重载(重点)
3.1 '+' 操作符重载
#include <iostream>
using namespace std;
class Integer{ //定义的整型类
private:
int a;
public:
Integer(int a):a(a){}
friend Integer operator + (const Integer& i1,const Integer& i2);
int show(){
return a;
}
};
Integer operator + (const Integer& i1,const Integer& i2)
{
return i1.a+i2.a; //值是int类型 Integer(int a)构造函数隐式触发
}
int main(){
int a=2,b=3;
cout<<a+b<<endl;
double c=1.2,d=1.3;
cout<<c+d<<endl;
Integer p1(10);
Integer p2(20);
Integer p3=p1+p2; //相当于operator+(p1,p2)的简写
cout<<p3.show()<<endl;
}
当有多个属性时,可以构造对象返回
#include <iostream>
using namespace std;
class Integer{ //定义的整型类
private:
int a;
int b;
public:
Integer(int a,int b):a(a),b(b){}
friend Integer operator + (const Integer& i1,const Integer& i2);
void show(){
cout<<a<<" "<<b<<endl;
}
};
Integer operator + (const Integer& i1,const Integer& i2)
{
int t=i1.a+i2.a;
int t2=i1.b+i2.b;
Integer temp (t,t2);
return temp;
}
int main(){
int a=2,b=3;
cout<<a+b<<endl;
double c=1.2,d=1.3;
cout<<c+d<<endl;
Integer p1(10,50);
Integer p2(20,50);
Integer p3=p1+p2;
p3.show();
}
3.2 前置++ 和 后置++
后置++需要用哑元参数区分和前置++
#include <iostream>
using namespace std;
class Integer{ //定义的整型类
private:
int a;
public:
Integer(int a):a(a){}
int show(){
return a;
}
friend Integer operator ++(Integer & t1);
friend Integer operator ++(Integer & t1,int);
};
Integer operator ++(Integer & t1){ //前置++
return ++t1.a;
}
Integer operator ++(Integer & t1,int){
return t1.a++;
}
int main(){
Integer i2(10);
//i2++;
cout<<(++i2).show()<<endl; //11
cout<<(i2++).show()<<endl; //11
cout<<i2.show()<<endl; //12
}
4.成员函数运算符重载(重点)
成员函数隐含this指针,可以指向当前对象,所以成员函数重载的参数都比友元函数重载少一个
#include <iostream>
using namespace std;
class Integer{ //定义的整型类
private:
int a;
public:
Integer(int a):a(a){}
//friend Integer operator + (const Integer& i1,const Integer& i2);
Integer operator + (const Integer& other);
// friend Integer operator ++(Integer & t1);
Integer operator ++(); //前置
// friend Integer operator ++(Integer & t1,int);
Integer operator ++(int); //后置
int show(){
return a;
}
};
Integer Integer::operator +(const Integer& other){
return this->a+other.a;
}
Integer Integer::operator ++(){ //前置++
cout<<"前置++"<<endl;
return ++this->a;
}
Integer Integer::operator ++(int){
cout<<"后置++"<<endl;
return this->a++;
}
int main(){
Integer i1(20);
Integer i2(30);
Integer i3=i1+i2; // 相当于i1.operator(i2)的简化
cout<<i3.show()<<endl;
cout<<(++i3).show()<<endl;
cout<<(i3++).show()<<endl;
}
5.其他运算符重载(掌握)
赋值运算符重载
写一个类的时候,默认会有构造函数,拷贝函数,析构,赋值函数。赋值运算符重载函数属于类内,是成员函数,所以它的重载只能通过显示的成员函数重载,不支持友元运算符重载。
默认的形式 T & operator =(const T &) //T是任意的类型
#include <iostream>
using namespace std;
class Integer{ //定义的整型类
private:
int a;
public:
Integer(int a):a(a){}
int show(){
return a;
}
Integer & operator =(const Integer & right);
};
Integer& Integer::operator =(const Integer & right){
cout<<"赋值运算符重载"<<endl;
this->a=right.a;
return *this;
}
int main(){
Integer i1(20);
Integer i2(30);
i1=i2; //默认是可以的。因为有默认的赋值运算符函数
cout<<i1.show();
}
注意:属性有指针类型的时候,需要赋值运算符重载。避免指针地址的值传递,不同对象属性用同一片内存区域,破坏对象之间的独立性。
类型转换运算符重载
可以使自定义类型的对象转换成任意类型。此函数也只能使用成员函数重载
#include <iostream>
using namespace std;
class Teacher{
private:
string subject;//科目
public:
Teacher(string subject)
{
this->subject=subject;
}
string get_subject(){
return subject;
}
operator string()
{
return subject;
}
};
int main(){
Teacher t1("C#");
cout<<t1.get_subject()<<endl;
string str="C";
Teacher t2=str;
cout<<t2.get_subject()<<endl;
string str2=t2;
cout<<str2<<endl;
}
注意事项●运算符重载限制在C++已有的运算符范围内,不允许创建新的运算符。
●运算符重载也是函数重载,运算符也是函数。
●重载之后的运算符不能改变优先级和结合性。
●重载之后的运算符不能改变操作数和语法结构。
●运算符重载不能改变该运算符用于基本数据类型的含义,但是可以把基本数据类型与自定义类型一起运算,或者都使用自定义类型。
●运算符重载是针对新类型数据的实际需要对原有运算符的功能进行扩充,因此重载之后的功能应该与原有的功能类似,避免没有目的地使用运算符重载。
●通常建议单目运算符(一个操作数)使用成员函数运算符重载,双目运算符使用友元函数运算符重载。