Day 12 C++面向对象编程 之 运算符重载
·
Day 12 C++面向对象编程 之 运算符重载
运算符重载概念:对已有的运算符进行重新定义,再赋予其另一种新的功能,以适应不同的数据类型;
对于内置的数据类型,编译器知道如何进行运算,但是编译器不知道自定义数据类型的数据的运算,比如一个类的两个对象的成员属性的运算,编译器就不知道怎么算;
可以自己写一个函数进行运算操作,编译器就写了一些通用名称:
一、加号运算符重载 operator+
实现两个自定义数据类型的相加运算:返回数据类型 operator+ (参数列表){运算操作}
//写在类中的
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;
}
...
//Person p3=p1.operator+(p2);
//可以简化为:
Person p3=p1+p2;
//通过全局函数重载“ + ”号
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 p3=operator+(p1,p2);
//也可以简化为:
Person p3=p1+p2;
- 成员函数重载的本质调用:Person p3=p1.operator+(p2);
- 全局函数重载的本质调用:Person p3=operator+(p1,p2);
- 运算符重载也可以发生函数重载,比如将第二个参数p2变成其他数据类型,比如int
Person operator+ (Person &p1,int m)
{
Person temp;
temp.m_a=p1.m_a+m;
temp.m_b=p1.m_b+m;
return temp;
}
Person p4=p1+77;
对于内资的数据类型(int、double、float…)的表达式的运算符是不可能改变的;不能滥用运算符重载(加就是加,不要把加法写成减法、乘法等等)
二、左移运算符重载 <<
可以输出自定义数据类型:返回数据类型 operator<<(ostream &cout, Person &p){操作}
class Person
{
public:
Person(int a, int b)
{
m_a = a;
m_b = b;
}
friend void operator<<(ostream& cout, Person& p);
private://一般将成员属性写为私有,若要赋值,写内部构造函数即可
//1.成员函数重载左移运算符
//void operator<< (cout)//p.operator<<(cout),简化版本是p<<cout,与预想的cout<<p不一致
//{
//所以一般不用成员函数重载左移运算符
//}
int m_a;
int m_b;
};
//2.全局函数重载左移运算符
void operator<<(ostream &cout, Person &p)//本质是operator<<(cout,p),简化版本是cout<<p
{
cout << "p.m_a="<<p.m_a << "p.m_b=" << p.m_b ;
}
//ostream &operator<<(ostream &cout, Person &p)//本质是operator<<(cout,p),简化版本是cout<<p
//{
// cout << "p.m_a="<<p.m_a << "p.m_b=" << p.m_b ;
// return cout;
//}
void test01()
{
Person p(7,6);
//p.m_a = 7;
//p.m_b = 6;
//cout << p.m_a << endl;
cout << p;//重载此符号,返回是void就不能追加别的,若是返回链式,将函数的返回值进行更改为ostream &,以及return cout即可
//cout << p << endl;
//cout<<p<<"Hello"<<endl;//后面可以无限追加
}
三、递增运算符重载(只可整型)
通过递增运算符,实现自己的整型数据:
class MyInteger
{
friend ostream& operator<<(ostream& cout, MyInteger myint);
public:
MyInteger()
{
m_Num = 7;
}
//1.成员函数重载前置++运算符
MyInteger &operator++()//一定返回引用,就是一直对本身进行修改,而不是对复制的值进行修改
{
m_Num++;//做++运算,再返回自身
return *this;
}
//1.成员函数重载后置++运算符
MyInteger operator++(int)//加了int,编译器就能知道这个是后置的++,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 test01()
{//前置
MyInteger myint;
cout << ++myint << endl;
}
void test02()
{//后置
MyInteger myint;
cout << myint++ << endl;
cout << myint << endl;
}
- 前置递增:先运算,后输出
- 后置递增:先输出,再运算
四、赋值运算符重载
C++编译器至少给一个类添加4个函数:
- 默认构造函数(无参,函数体为空);
- 默认析构函数(无参,函数体为空);
- 默认拷贝构造函数,对属性进行值拷贝;
- 赋值运算符 operator=,对属性进行值拷贝
class Person
{
public:
Person(int age)
{
m_Age=new int(age);//将传入进来的age参数,进行new化,并将值存放在堆区,然后用本对象的m_Age指向那个堆区的age
}
//因为存放在堆区,所以堆区的数据需要程序员手动创造,手动释放,在析构函数中释放
~Person()
{
if (m_Age != NULL)//判断是否为空,若不为空,则释放
{
delete m_Age;
m_Age = NULL;
}
}
void operator=(Person &p)//Person& operator(Person &p)
{
//先判断是否有属性存放在堆区,如果有,则先释放干净,再进行深拷贝
if (m_Age != NULL)
{
delete m_Age;
m_Age = NULL;
}
m_Age = new int(*p.m_Age);//深拷贝
//return *this;//若想要连等,就返回对象本身
}
int *m_Age;//将年龄写成一个指针,用指针指向,年龄真实的值存放在堆区,指针指向堆区里面的地址空间
};
void test01()
{
Person p1(22);
Person p2(18);
p2 = p1;
//这个复制是简单的值拷贝,因为m_Age是指针,这个属性内部存放是数值在堆区中的位置,所以,p2复制的就是位置地址那一串数字,而在执行析构函数时,这个地址指向的空间在第一次析构时就已经被释放了,在第二次析构时,就会造成非法访问
//利用深拷贝解决这个浅拷贝的问题,在堆区重新开辟一片空间,存放和p1指向的值一模一样的值,然后p2指向那个新的空间,自己写等号赋值运算
cout << "p1的年龄:" << *p1.m_Age<<endl;//因为p1.m_Age是一个指针,若要取得内部的指向的值,要进行解引用*
cout << "p2的年龄:" << *p2.m_Age << endl;
}
p2 = p1;
这个复制是简单的值拷贝,因为m_Age是指针,这个属性内部存放是数值在堆区中的位置,所以,p2复制的就是位置地址那一串数字,而在执行析构函数时,这个地址指向的空间在第一次析构时就已经被释放了,在第二次析构时,就会造成非法访问。利用深拷贝解决这个浅拷贝的问题,在堆区重新开辟一片空间,存放和p1指向的值一模一样的值,然后p2指向那个新的空间。
内置数据类型允许连等的操作,比如c=b=a,重载的话,若返回值是void就不可以连等,但是给返回值类型改为返回对象本身(return *this),就可以进行连等
五、关系运算符重载(==、!=)
让两个自定义对象进行对比操作
class Person
{
public:
Person(string name,int age)
{
m_Name = name;
m_Age = age;
}
//1.成员函数重载
bool operator==(Person& p)
{
if (this->m_Name == p.m_Name && this->m_Age == p.m_Age)
{
return true;
}
return false;
}
string m_Name;
int m_Age;
};
void test01()
{
Person p1("懒羊羊", 10);
Person p2("喜羊羊", 13);
if (p1 == p2)
{
cout << "二者相同" << endl;
}
else
{
cout << "二者不同" << endl;
}
}class Person
{
public:
Person(string name,int age)
{
m_Name = name;
m_Age = age;
}
//1.成员函数重载
bool operator==(Person& p)
{
if (this->m_Name == p.m_Name && this->m_Age == p.m_Age)
{
return true;
}
return false;
}
//重载!=
bool operator!=(Person& p)
{
if (this->m_Name == p.m_Name && this->m_Age == p.m_Age)
{
return false;
}
return true;
}
string m_Name;
int m_Age;
};
void test01()
{
Person p1("懒羊羊", 10);
Person p2("喜羊羊", 13);
if (p1 == p2)
{
cout << "二者相同" << endl;
}
else
{
cout << "二者不同" << endl;
}
if (p1 != p2)
{
cout << "二者不相同" << endl;
}
else
{
cout << "二者相同" << endl;
}
}
六、函数调用运算符重载
- 函数调用运算符()也可以重载
- 由于重载后使用的方式非常想函数的调用,因此称为仿函数
- 仿函数没有固定写法,非常灵活
class MyPrint
{
public:
//重载函数调用运算符
void operator()(string test)
{
cout << test << endl;
}
};
void MyPrint22(string test)
{
cout << test << endl;
}
//非常灵活,没有固定写法,这里写成一个加法,依照需求可以写成各种各样的
class MyAdd
{
public:
int operator()(int a, int b)
{
return a + b;
}
};
void test01()
{
MyPrint myPrint;
myPrint("你好!懒羊羊!!");//使用起来非常像一个函数,所以称为仿函数
MyPrint22("你好!懒羊羊!!");//函数
MyAdd myadd;
int ret = myadd(7, 7);
cout << ret << endl;
}
匿名函数对象
cout<<MyAdd()(7,7)<<endl;//类型+括号,是匿名对象,后面再加一个括号是一个函数,变成匿名函数对象
更多推荐
所有评论(0)