C++学习笔记
----运算符重载
文章目录
一.运算符重载的概念
- 概念
运算符重载是为已有的运算符定义新的运算功能。
返回类型 operator 运算符符号(参数说明);
- 特点
(1)只能重载已经存在的C++运算符
(2)运算符重载不能改变运算符操作数的个数,优先级和结合性,也不能改变运算符原有的语法结构
(3)运算符的操作数必须至少有一个某个类的类对象,否则不能对运算符进行重载
(4)不能重载的运算符:
名称 | 符号 |
---|---|
间接访问对象的指针成员的运算符 | .*, ->* |
类对象(点操作)运算符 | . |
条件运算符 | ?: |
作用域运算符 | :: |
长度运算符 | sizeof |
预处理符号 | # |
(5)运算符重载函数不能带缺省参数值并可通过以下两种形式实现:类成员函数和类友元函数
- 运算符重载函数作为类的成员函数
(1)运算符重载成员函数要求左操作数一定必须是类对象。除了赋值运算符=以外,基类的运算符重载成员函数都可由派生类继承。
(2)运算符重载成员函数参数的个数要比运算符实际操作数少一。
返回数据类型 operator 运算符符号(参数表);
...
返回数据类型 X::operator 运算符符号(参数表){
函数体
}
- 运算符重载函数作为类的友元函数
(1)赋值运算符=, 下标运算符[ ], 成员选择运算符->, 函数调用运算符( ), new和delete运算符只能用成员函数不能用友元函数重载。
(2)运算符重载友元函数参数的个数与运算符的实际操作个数相同。
class X{
...
friend 返回数据类型 operator 运算符符号(参数表);
...
};
- 不利用运算符重载/利用运算符重载实现复数相加
#include <iostream>
using namespace std;
class CComplex{
private:
int a, b;
public:
CComplex(int n1, int n2):a(n1),b(n2){ }
CComplex & addComplex(const CComplex &c){ //返回类型可以为void
a += c.a;
b += c.b;
return *this;
}
CComplex & operator +=(const CComplex &c){ //返回类型可以为void
a += c.a;
b += c.b;
return *this;
}
void print(){ cout << "C1+C2 = " << a << " + " << b << "i" << endl; }
void printC(){ cout << "Now C1 = " << a << " + " << b << "i" << endl; }
};
int main()
{
CComplex C1(1,2);
CComplex C2(2,1);
C1.addComplex(C2);
C1.print();
C1.printC();
C1 += C2;
C1.print();
return 0;
}

二.一元运算符重载
- 一元减运算符,即负号( - )和逻辑非运算符( ! )
#include <iostream>
using namespace std;
class Point{
private:
int x, y;
public:
Point(int n1, int n2):x(n1),y(n2){ }
Point operator -(){
x = -x;
y = -y;
return Point(x, y);
}
Point & operator !(){
x = !x;
y = !y;
return *this;
}
void print(){ cout << "x = " << x << " y = " << y << endl; }
};
int main()
{
Point P(1, -1);
-P;
P.print();
!P;
P.print();
return 0;
}

- 递增运算符( ++ )和递减运算符( - - )
#include <iostream>
using namespace std;
class Point{
private:
int x, y;
public:
Point(int n1, int n2):x(n1),y(n2){ }
Point & operator ++(){ //前缀递增
++x;
++y;
return *this;
}
Point operator ++(int){ //后缀递增
Point p(x, y);
++x;
++y;
return p; //返回旧值
}
void print(){ cout << "x = " << x << " y = " << y << endl; }
};
int main()
{
Point P(1, -1);
cout << "P: ";
P.print();
++P;
cout << "++P: ";
P.print();
P++;
cout << "P++: ";
P.print();
return 0;
}

三.二元运算符重载
- 加运算符( + )、减运算符( - )、乘运算符( * )和除运算符( / )
#include <iostream>
using namespace std;
class Point{
private:
int x, y;
public:
Point(int n1, int n2):x(n1),y(n2){ }
Point operator +(const Point &p){
Point P(0, 0);
P.x = p.x + x;
P.y = p.y + y;
return P;
}
Point operator -(const Point &p){
Point P(0,0);
P.x = x - p.x;
P.y = y - p.y;
return P;
}
Point operator *(const Point &p){
Point P(0,0);
P.x = p.x * x;
P.y = p.y * y;
return P;
}
Point operator /(const Point &p){
Point P(0,0);
P.x = x / p.x;
P.y = y / p.y;
return P;
}
void print(){ cout << "x = " << x << " y = " << y << endl; }
};
int main()
{
Point P1(1, 2);
Point P2(3, 4);
Point P(0, 0);
P = P1 + P2;
cout << "P1+P2: ";
P.print();
P = P1 - P2;
cout << "P1-P2: ";
P.print();
P = P1 * P2;
cout << "P1*P2: ";
P.print();
P = P2 / P1;
cout << "P2/P1: ";
P.print();
return 0;
}

四.关系运算符重载
- < 、 > 、 <= 、 >= 、 ==、!= 等等,返回类型一般定义为bool或者int
#include <iostream>
using namespace std;
class Point{
private:
int x, y;
public:
Point(int n1, int n2):x(n1),y(n2){ }
bool operator >(const Point &p){
if(x>p.x && y>p.y)
return 1;
return 0;
}
bool operator >=(const Point &p){
if(x>=p.x && y>=p.y)
return 1;
return 0;
}
bool operator <(const Point &p){
if(x<p.x && y<p.y)
return 1;
return 0;
}
bool operator <=(const Point &p){
if(x<=p.x && y<=p.y)
return 1;
return 0;
}
bool operator ==(const Point &p){
if(x==p.x && y==p.y)
return 1;
return 0;
}
bool operator !=(const Point &p){
if(x!=p.x || y!=p.y)
return 1;
return 0;
}
};
int main()
{
Point P1(1, 2);
Point P2(3, 4);
if(P1 < P2)
cout << "P1<P2" << endl;
if(P1 <= P2)
cout << "P1<=P2" << endl;
if(P1 > P2)
cout << "P1>P2" << endl;
if(P1 >= P2)
cout << "P1>=P2" << endl;
if(P1 == P2)
cout << "P1=P2" << endl;
if(P1 != P2)
cout << "P1!=P2" << endl;
return 0;
}

五.输入/输出运算符重载
- 流提取运算符 >> 和流插入运算符 << ,只能以友元函数的形式实现
原因:"<<“插入运算符的左操作数cout是C++流类库ostream类的类对象,”>>"插入运算符的左操作数cin是C++流类库istream类的类对象,无法更改C++类库中ostream和istream类的定义。
friend ostream &operator <<(ostream &out, 类对象&r);
返回类型为ostream的类对象引用是为了使<<运算符可以级联
friend istream &operator <<(istream &in, 类对象&r);
返回类型为istream的类对象引用是为了使>>运算符可以级联
#include <iostream>
using namespace std;
class Point{
private:
int x, y;
public:
Point(int n1=0, int n2=0):x(n1),y(n2){ }
friend ostream &operator<<(ostream &output, const Point &P)
{
output << "x = " << P.x << ", y = " << P.y;
return output;
}
friend istream &operator>>(istream &input, Point &P) //不可const Point &P
{
input >> P.x >> P.y;
return input;
}
};
int main()
{
Point P1(1, 2);
Point P2;
cin >> P2;
cout << "P1: " << P1 << endl;
cout << "P2: " << P2 << endl;
return 0;
}

六.赋值运算符重载
- 可以重载赋值运算符( = ),用于创建一个对象,比如拷贝构造函数。
#include <iostream>
using namespace std;
class Point {
private:
int x, y;
public:
Point(int n1=0, int n2=0) : x(n1), y(n2) {}
void operator =(const Point &p){
x = p.x;
y = p.y;
}
void print(){
cout << "x = " << x << " y = " << y << endl;
}
};
int main(){
Point p(1, 2);
Point cP;
cP = p;
cP.print();
return 0;
}

七.函数调用运算符 () 重载
- 当重载 () 时,不是创造了一种新的调用函数的方式,而是创建一个可以传递任意数目参数的运算符函数。
#include <iostream>
using namespace std;
class Point {
private:
int x, y;
public:
Point(int n1=0, int n2=0) : x(n1), y(n2) {}
Point operator ()(int a, int b){
Point p;
p.x = x + a;
p.y = y + b;
return p;
}
void print(){
cout << "x = " << x << " y = " << y << endl;
}
};
int main(){
Point p(1, 2);
Point cP;
cP = p(1, 1);
cP.print();
return 0;
}

八.下标运算符 [] 重载
- 用于访问数组元素。重载 [ ] 用于增强操作 C++ 数组的功能。
#include <iostream>
using namespace std;
class Point {
private:
int x, y;
public:
Point(int n1=0, int n2=0) : x(n1), y(n2) {}
void setx(int n1){ x=n1; }
void sety(int n2){ y=n2; }
void print(){
cout << "x = " << x << " y = " << y << endl;
}
};
class Area{
private:
Point p[3];
public:
Area(){ }
void setPoint(int n1, int n2, int i){ p[i].setx(n1); p[i].sety(n2); }
Point &operator[](int i){
if(i > 2 || i < 0) {
cout << "error\n";
return p[0];
}
else
return p[i];
}
};
int main(){
int n1, n2;
Area area;
for(int i=0; i < 3; i++){
cin >> n1 >> n2;
area.setPoint(n1, n2, i);
}
cout << "area[0] = ";
area[0].print();
cout << "area[1] = ";
area[1].print();
cout << "area[2] = ";
area[2].print();
cout << "area[3] = ";
area[3].print();
return 0;
}

九.类成员访问运算符 -> 重载
- 用于为一个类赋予"指针"行为。运算符 -> 必须是一个成员函数。如果使用了 -> 运算符,返回类型必须是指针或者是类的对象。
- 运算符 -> 通常与指针引用运算符 * 结合使用,用于实现"智能指针"的功能。这些指针是行为与正常指针相似的对象,唯一不同的是,当通过指针访问对象时,它们会执行其他的任务。比如,当指针销毁时,或者当指针指向另一个对象时,会自动删除对象。
摘自菜鸟教程
#include <iostream>
#include <vector>
using namespace std;
// 假设一个实际的类
class Obj {
static int i, j;
public:
void f() const { cout << i++ << endl; }
void g() const { cout << j++ << endl; }
};
// 静态成员定义
int Obj::i = 10;
int Obj::j = 12;
// 为上面的类实现一个容器
class ObjContainer {
vector<Obj*> a;
public:
void add(Obj* obj)
{
a.push_back(obj); // 调用向量的标准方法
}
friend class SmartPointer;
};
// 实现智能指针,用于访问类 Obj 的成员
class SmartPointer {
ObjContainer oc;
int index;
public:
SmartPointer(ObjContainer& objc)
{
oc = objc;
index = 0;
}
// 返回值表示列表结束
bool operator++() // 前缀版本
{
if(index >= oc.a.size() - 1) return false;
if(oc.a[++index] == 0) return false;
return true;
}
bool operator++(int) // 后缀版本
{
return operator++();
}
// 重载运算符 ->
Obj* operator->() const
{
if(!oc.a[index])
{
cout << "Zero value";
return (Obj*)0;
}
return oc.a[index];
}
};
int main() {
const int sz = 3;
Obj o[sz];
ObjContainer oc;
for(int i = 0; i < sz; i++)
{
oc.add(&o[i]);
}
SmartPointer sp(oc); // 创建一个迭代器
do {
sp->f(); // 智能指针调用
sp->g();
} while(sp++);
return 0;
}

十.类型转换
- 可将一种类型的数据转换成另外一种类型的数据。根据内部基本数据类型和用户自定义类类型,存在四种类型转换:
不同内部基本类型之间的转换
不同用户自定义类类型之间的转换
内部类型转换为用户自定义类类型
用户自定义类类型转换为内部类型
- 不同内部基本数据类型的转换方法:隐式转换和显示转换
(1)自动(隐式)转换
- 混合运算:不同内部基本类型变量构成的表达式中,级别低的数据类型自动向级别高的数据类型转换。如float自动转换为double。
int a = 10;
double b = 10.7;
double c = a+b; //c=20.7,a会被自动转换为double类型,用转换的结果再与b相加
- 赋值运算:表达式的值的类型自动转换为形参的类型。
int f = true; //bool类型被转换为int类型
- 实参传值给形参:实参的值的类型自动转换为形参的类型。
void func(double a){ ... }
func(1); //int类型被转换为double类型
- 函数返回结果:return表达式的值的类型自动转换为函数的返回类型。
double add(int n1, int n2){
return (n1+n2); //int类型被转换为double类型
}
(2)显示转换
- 强制法:用强制类型转换运算符,(类型名)表达式。
double a = 10.7;
int b = (int)a; //double类型强制转换为int
- 调用类型转换函数,类型名(表达式)。
- 内部基本数据类型转换为用户自定义类类型
(1)通过对赋值运算符在用户自定义的类中以成员函数的形式进行重载,可以在对类对象赋值的赋值语句中将右值表达式的类型转换为类类型,此方法要求赋值运算符重载成员函数参数的类型与右值表达式的类型相同。
#include <iostream>
using namespace std;
class CPoint{
private:
int x, y;
public:
CPoint(int x_, int y_):x(x_),y(y_){ }
void operator =(int n){
x=n;
y=n;
}
friend ostream &operator <<(ostream &output, CPoint &p){
output << "(" << p.x << ", " << p.y << ")" << endl;
return output;
}
};
int main() {
CPoint p(2, 3);
cout << "p" << p;
int n=5;
p = n;
cout << "Now p" << p;
return 0;
}

(2)在用户自定义的类中增加一个参数类型为内部基本数据类型的构造函数,通过该构造函数将内部基本数据类型的变量转换成一个const类对象。(转换构造函数)
#include <iostream>
using namespace std;
class Point{
private:
int x, y;
public:
Point(){ }
Point(int n1, int n2):x(n1),y(n2){ }
Point(int n1):x(n1),y(n1){ } //转换构造函数
void print(){
cout << "x = " << x << ", y = " << y << endl;
}
};
int main(){
Point p(1,2);
cout << "p = ";
p.print();
p = 1; //调用构造函数,将int转换成对象Point
cout << "p = ";
p.print();
Point cP(1); //调用构造函数,将int转换成对象Point
cout << "cP = ";
cP.print();
return 0;
}

- 类类型转换为内部基本数据类型或其他类类型
可以在用户自定义的类中定义一个类型转换成员函数将该类对象转换成标准类型变量或者是另外一个类的对象,类型转换成员函数的格式为:
operator type() //type为内部基本数据类型
{
...
return data; //data是type类型的数据,把本类对象转换成type类型变量
}
operator ctype() //ctype为另外一个类类型
{
...
return ctype(data参数); //把data参数传给ctype类的构造函数,把本类对象转换成ctype类对象
}
#include <iostream>
#include <cstdlib>
using namespace std;
class CPoint{
private:
int x, y;
public:
CPoint(int n1=0, int n2=0):x(n1), y(n2){ }
int getX(){ return x; }
int getY(){ return y; }
};
class CRectangle{
private:
CPoint leftPoint;
CPoint rightPoint;
public:
CRectangle(int x1, int y1, int x2, int y2):leftPoint(x1, y1), rightPoint(x2, y2){ }
operator int(){
int area;
area = (leftPoint.getY()-rightPoint.getY())*(rightPoint.getX()-leftPoint.getX());
return area;
}
friend ostream &operator<<(ostream &output, CRectangle &r){
output << r.leftPoint.getX() << " " << r.leftPoint.getY() << " ";
output << r.rightPoint.getX() << " " << r.rightPoint.getY();
return output;
}
};
int main() {
int t, x1, x2, y1, y2;
cin >> t;
while(t--){
cin >> x1 >> y1 >> x2 >> y2;
CRectangle rect1(x1, y1, x2, y2);
cin >> x1 >> y1 >> x2 >> y2;
CRectangle rect2(x1, y1, x2, y2);
cout << "矩形1:" << rect1 << " " << (int)rect1 << endl;
cout << "矩形2:" << rect2 << " " << (int)rect2 << endl;
}
return 0;
}

- 类类型转换规则
当运算符的两个操作数一个为本类对象,另一个为内部基本类型的变量或另一个类的类对象,会按照下述规则自动隐式地进行类型转换:
- 如果本类不存在该运算符的重载函数,则会调用类型转换成员函数将本类对象转换成非本类对象;
- 如果本类存在该运算符的重载函数且与非本类对象对应位置的形参类型为非const对象引用时,由于转换构造函数生成的对象为const对象而无法作为实参初始化非const对象引用形参,此时会调用类型转换成员函数将本类对象转换成非本类对象;
- 如果本类存在该运算符的重载函数且与非本类对象对应位置的形参类型为const对象引用时,则存在二义性即可以调用转换构造函数将非本类对象或内部变量转换成本类对象,也可以调用类型转换成员函数将本类对象转换成非本类对象或内部变量。此时转换构造函数和类型转换成员函数不能同时出现在本类的声明中。