C++的面向对象学习(6):运算符的重载

文章目录

  • 前言:什么是运算符重载?针对自定义的类与对象类型。
  • 一、加号的运算符重载
    • 1.引入背景
    • 2.所以运算符重载的作用:
    • 3.实现对象间的相加代码:+号运算符重载
      • ①在类中实现加号运算符重载
      • ②设计全局函数实现加号运算符重载
      • ③改写函数,实现对象与数值之间的相加
  • 二、左移与右移的运算符重载
    • 1.引入背景
    • 2.成员函数实现左移运算符重载
      • ①全局函数实现左移重载
      • ②成员变量一般是私有的,无法让全局函数访问,怎么办呢?
  • 三、递增运算符++的重载
    • 1.引入背景
    • 2.对成员变量实现前置递增和后置递增
  • 四、赋值运算符=的重载
    • 1.引入背景
    • 2.代码实现
  • 五、关系运算符(>、<、==、!=)的重载
    • 1.引入背景
    • 2.代码实现
  • 六、函数调用运算符()的重载
    • 1.背景引入
    • 2.代码例子:


前言:什么是运算符重载?针对自定义的类与对象类型。

运算符重载是C++中一种强大的特性,它允许我们为自定义的类类型定义运算符的行为。通过运算符重载,我们可以使用自定义的类对象像使用内置类型一样进行运算。

以下是一些常见的运算符重载示例:

算术运算符重载:+、-、*、/等运算符可以被重载,使得两个对象可以进行相应的运算。

关系运算符重载:==、!=、<、>、<=、>=等运算符可以被重载,用于比较两个对象的关系。

赋值运算符重载:=运算符可以被重载,用于将一个对象的值赋给另一个对象。

下标运算符重载:[]运算符可以被重载,用于实现类似数组的下标访问操作。

函数调用运算符重载:()运算符可以被重载,使得对象可以像函数一样被调用。

输入输出运算符重载:<<和>>运算符可以被重载,用于实现对象的输入和输出。

一、加号的运算符重载

1.引入背景

C++的面向对象学习(6):运算符的重载_第1张图片
C++的面向对象学习(6):运算符的重载_第2张图片
如果我想实现两个对象相加后,对应的变量之和等于第三个对象的该变量,直接相加肯定是不行的,会报错。
如果用常规的:
在这里插入图片描述
可以到是可以,但是如果人很多,从p1、p2、p3、…p100,我们想求出其总和放到一个对象中去,或者涉及很多字符串型变量的拼接,根本不可能一一写出来。另外,如果想用for循环来做累加操作也不行,因为对象名没有所谓的序号,不能用i来代替每一个对象。所以这就体现出运算符重载的作用了。

2.所以运算符重载的作用:

让类的对象这一特殊的类型,也具有加减乘除、赋值、比较、数组化的能力。

3.实现对象间的相加代码:+号运算符重载

①在类中实现加号运算符重载

class person {
public:
    int age;
    int money;

    person operator+(const person& other) {
        person result;
        result.age = this->age + other.age;
        result.money = this->money + other.money;
        return result;
    }
};

(1)在类中重载加法运算符时,我们定义了一个名为operator+的成员函数。这个函数接受一个person对象的引用作为参数,表示要与当前对象相加的另一个对象。函数体内,创建了一个新的person对象result,用于存储相加的结果。然后将当前this指针指向的对象的年龄和另一个对象的年龄相加,并将结果赋值给result对象的年龄成员变量。
最后,返回result对象就实现了将两个person对象的年龄和钱相加,并将结果作为新的person对象返回。

(2)通过自己写成员函数,就实现了两个对象的各个属性相加后返回新的对象功能。

(3)operator+是什么?
就是一个函数名称,这是person类的成员函数。在主函数中,我们可以这样调用:

int main()
{
	person p1;
	p1.age = 15;
	p1.money = 1000;
	person p2;
	p2.age = 18;
	p2.money = 1500;

	//想实现第三个对象p3的年龄和钱是p1p2的加和
	person p3 = p1.operator+(p2);

	
	system("pause");
}

但是编译器很聪明,他看到operator+这个形式,便可以直接简化理解成:

//想实现第三个对象p3的年龄和钱是p1p2的加和
	person p3 = p1+p2;

在重载的加法运算符函数中,关键字 this 指向当前对象,也就是 p1。而参数 other 则指向传递进来的对象,也就是 p2。在函数内部,this 指向 p1,other 指向 p2。

②设计全局函数实现加号运算符重载

person operator+(person& p1, person& p2) {
	person res;
	res.age = p1.age + p2.age;
	res.money = p1.money + p2.money;
	return res;
}

int main()
{
	person p1;
	p1.age = 15;
	p1.money = 1000;
	person p2;
	p2.age = 18;
	p2.money = 1500;

	//全局函数的加号重载调用
	person p4 = p1 + p2;
	system("pause");
}

③改写函数,实现对象与数值之间的相加

//实现一个对象与常数相加的重载
person operator+(person& p1, int num) {
	person val;
	val.age = p1.age + num;
	val.money = p1.money + num;
	return val;
}

二、左移与右移的运算符重载

1.引入背景

常规的左移运算符 << 和右移运算符 >> 是 C++ 中的位运算符,用于对流对象进行输入和输出操作。它们也可以被重载以实现自定义的输入和输出行为。

string myname="xiaoming";
cout<<"hello world"<<myname<<endl;
int val;
cin>>val;

现在我有个需求,对于一个类的对象:
C++的面向对象学习(6):运算符的重载_第3张图片

person p1;
p1.age=18;
p1.money=5000;
cout<<p<<endl;

我想让cout可以直接输出p的所有成员属性,显然目前的语法规则是不允许的,我们只能单独一项一项的打印输出。
重载左移运算符 << 可以用于 自定义对象 的输出操作,使其能够以自定义的方式输出到流对象中。重载右移运算符 >> 则可以用于 自定义对象 的输入操作,使其能够以自定义的方式从流对象中读取数据。

2.成员函数实现左移运算符重载

①全局函数实现左移重载

C++的面向对象学习(6):运算符的重载_第4张图片

如果把cout和对象p作为函数的两个传参的话,那首先得明确cout到底是个什么类型的变量?

cout 是 C++ 标准库中的一个对象,它是 std::ostream 类型的变量。std::ostream 是一个输出流类,用于将数据输出到不同的目标,比如控制台、文件等。
cout 对象是 std::ostream 类的一个实例,它定义了一系列的成员函数,如 operator<<,用于将不同类型的数据输出到流中。通过使用 << 运算符,我们可以将数据插入到 cout 流中,并将其输出到标准输出设备(通常是控制台)。

C++的面向对象学习(6):运算符的重载_第5张图片
osteam是输出流类的一个对象,全局只有一个,用引用传值。

//实现一个对象与cout的左移运算符重载
ostream& operator<<(ostream& out, person& p) {

	out << p.age << endl;
	out << p.money << endl;
	return out;
}

out是类ostream的引用对象,把结果又引用返回给cout这个唯一的对象。

②成员变量一般是私有的,无法让全局函数访问,怎么办呢?

上一节说了,想让函数访问私有成员变量,有很多办法。
首先可以在类里面写函数接口,但是变量多起来就比较麻烦。

另外就是使用友元声明。可以直接用friend声明友元类或者友元全局函数,就可以直接访问私有变量了。而友元类的成员函数去访问另一个类的私有变量上次编译器总是报错,就不推荐了。

class person{
friend ostream& operator<<(ostream& out, person& p);
private:
	int a;
	int b;
public:
	person(int x,int y){
		a=x;
		b=y;
	}
}

//实现一个对象与cout的左移运算符重载
ostream& operator<<(ostream& out, person& p) {

	out << p.age1 << endl;
	out << p.money1 << endl;
	return out;
}

int main(){
	person p1(1,2);
	cout<<p1<<endl;
}

三、递增运算符++的重载

1.引入背景

实现:实例化一个对象后,可以直接对该对象进行前置++和后置++的操作。

person p1;
p1++;
++p1;

2.对成员变量实现前置递增和后置递增

class Counter {
public:
    Counter() : count(0) {}
    // 前置递增运算符
    Counter& operator++() {
        ++count;
        return *this;
    }

    // 后置递增运算符
    Counter operator++(int) {
        Counter temp = *this;
        ++count;
        return temp;
    }

private:
    int count;
};

前置递增运算符(++)重载函数返回一个引用,它将计数器的值加1,并返回自身的引用。这样,我们可以使用前置递增运算符来直接对计数器对象进行递增操作。

后置递增运算符(++)重载函数接受一个额外的int参数(通常不使用),用于区分前置递增和后置递增。它首先创建一个临时的Counter对象temp,将当前计数器的值赋给temp,然后将计数器的值加1,并返回temp对象。这样,我们可以使用后置递增运算符来对计数器对象进行递增操作,同时还可以获取递增前的值。
主程序里:

Counter counter;
++counter;  // 前置递增
int previousValue = counter++;  // 后置递增,并获取递增前的值

四、赋值运算符=的重载

1.引入背景

实现:实例化两个对象后,可以直接让第二个对象赋值给第一个对象。

person p1;
person p2;
p2=p1;

2.代码实现

	//赋值运算符重载
	person& operator=(const person& other) {
		if (this != &other) {
			count = other.count;
		}
		return *this;
	}

person& operator=(const person& other): 这是赋值运算符重载函数的声明,它指定了函数的返回类型为person&,表示返回一个对当前对象的引用。参数other是一个常量引用,表示要赋值的另一个person对象。

if (this != &other): 这是一个条件判断语句,用于检查是否是自我赋值。this是指向当前对象的指针,&other是指向另一个对象的指针。如果它们不相等,说明不是自我赋值,可以进行赋值操作。

count = other.count;: 这行代码将另一个对象other的count成员变量的值赋给当前对象的count成员变量。这是一个简单的赋值操作,用于复制对象的状态。

return this;: 这行代码返回对当前对象的引用,以支持链式赋值操作。通过返回this,可以允许连续多次赋值,例如p1 = p2 = p3;。

五、关系运算符(>、<、==、!=)的重载

1.引入背景

要实现对象的关系比较

person p1;
person p2;

p1>p2;
p1==p2;
p1!=p2;

2.代码实现

class person {
public:
    // 构造函数和其他成员函数的实现
	person(int a):count(a){}

    bool operator>(const person& other) const {
        if(this->count > other.count){
        	return true;
        }
        else return false;
    }

    bool operator<(const person& other) const {
        return count < other.count;
    }

    bool operator==(const person& other) const {
        return count == other.count;
    }

    bool operator!=(const person& other) const {
        return count != other.count;
    }

private:
    int count;
    // 其他成员变量
};
 
int main(){
	person p1(2);
	person p2(5);

// 使用关系运算符进行比较
if (p1 > p2) {
    // p1的count大于p2的count
} else if (p1 < p2) {
    // p1的count小于p2的count
} else if (p1 == p2) {
    // p1的count等于p2的count
} else {
    // p1的count不等于p2的count
}
}

六、函数调用运算符()的重载

1.背景引入

函数调用运算符的重载常常用于创建仿函数(Functor)。仿函数是一种行为类似于函数的对象,可以像函数一样被调用。

2.代码例子:

下面是一个示例,展示了如何创建一个简单的仿函数:

class Adder {
public:
    int operator()(int x, int y) const {
        return x + y;
    }
};

int main() {
    Adder add;
    int result = add(3, 4); // 调用仿函数
    // result 等于 7
    return 0;
}

在上述示例中,我们定义了一个名为Adder的类,并在其中重载了函数调用运算符()。在重载函数中,我们将两个整数相加,并返回结果。

在main函数中,我们创建了一个Adder对象add,并使用add(3, 4)的语法来调用仿函数。这将返回3 + 4 = 7的结果,并将其存储在result变量中。

仿函数可以像函数一样接受参数,并返回结果。它们可以用于算法函数、STL容器和其他需要函数对象的场景。

你可能感兴趣的:(c++,学习,java)