目录
定义
运算符重载是一种特性,对已有的运算符重新进行定义,赋予其另一种功能,以适应不同的数据类型,
operator
是一个关键字,用于表示运算符重载。运算符重载允许我们定义自定义类型的运算符行为,使其能够像内置类型一样使用。C++ 中的运算符包括算术运算符(例如+
、-
、*
、/
)、比较运算符(例如==
、!=
、>
、<
)、逻辑运算符(例如&&
、||
、!
)等等。
在C++中,可以通过成员函数或非成员函数实现运算符重载。成员函数重载运算符时,该函数将作为类的成员,并且至少有一个操作数是该类的对象。非成员函数重载运算符时,它们没有隐式的this指针参数,并且操作数可以是任意类型。
注意
对于内置的数据类型的表达式的的运算符是不可能改变的
不要滥用运算符重载
在成员函数中使用
this
指针是可选的,如果没有命名冲突的情况,可以省略this->
,直接使用成员变量和成员函数的名称即可。
举例
加号运算符重载(+)
可以实现两个自定义数据类型相加的运算
成员函数重载
#include <iostream>
class MyClass {
private:
int value;
public:
MyClass(int v) : value(v) {}
MyClass operator+(const MyClass& other) const {
return MyClass(value + other.value);
}
int getValue() const {
return value;
}
};
int main() {
MyClass a(5);
MyClass b(10);
MyClass c = a + b;
std::cout << "c = " << c.getValue() << std::endl; // 输出:c = 15
return 0;
}
在上面的示例中,我们定义了一个名为MyClass的类,并重载了加号运算符(+)作为成员函数。该成员函数接受一个参数,表示需要与当前对象相加的另一个对象。在函数体内,我们执行了加法运算并返回一个新的MyClass对象。
非成员函数重载
#include <iostream>
class MyClass {
private:
int value;
public:
MyClass(int v) : value(v) {}
int getValue() const {
return value;
}
friend MyClass operator+(const MyClass& obj1, const MyClass& obj2);
};
MyClass operator+(const MyClass& obj1, const MyClass& obj2) {
return MyClass(obj1.value + obj2.value);
}
int main() {
MyClass a(5);
MyClass b(10);
MyClass c = a + b;
std::cout << "c = " << c.getValue() << std::endl; // 输出:c = 15
return 0;
}
在上面的示例中,我们定义了一个名为MyClass的类,并使用friend关键字声明了非成员函数operator+()。在函数体内,我们执行了加法运算并返回一个新的MyClass对象。
总结
我们可以使用
+
运算符来调用 MyClass 对象的成员函数operator+
。例如,a+ b
实际上相当于调用了 a.operator+(b)
,其中 a是左操作数,b 是右操作数。
无论是成员函数重载还是非成员函数重载,都可以实现相同的功能。选择使用哪种方式主要取决于代码的结构和设计需求。重载加号运算符的结果是使得MyClass对象可以直接使用加号进行相加操作,从而提高代码的可读性和可维护性
左移运算符重载(<<)
左移运算符(<<)用于输出信息到流对象中。我们可以重载左移运算符,使自定义类型也能够通过左移运算符进行输出操作。
成员函数重载
#include <iostream>
class MyClass {
private:
int value;
public:
MyClass(int v) : value(v) {}
int getValue() const {
return value;
}
MyClass& operator<<(std::ostream& os) const {
os << "Value: " << value;
return *this;
}
};
int main() {
MyClass obj(10);
obj << std::cout << std::endl; // 输出:Value: 10
return 0;
}
在这个示例中,我们在
MyClass
类中定义了一个成员函数operator<<
,该函数接受一个类型为std::ostream&
的输出流对象os
作为参数,并返回一个MyClass&
类型的引用。在函数定义中,我们将要输出的信息以指定的格式写入到给定的流对象
os
中。在这个示例中,我们将value
的值输出到流对象中。然后,在
main
函数中,我们创建了一个MyClass
对象obj
,并通过调用obj << std::cout
来输出对象的值到标准输出流。这样,我们就可以使用成员函数方式重载左移运算符(<<)来输出
MyClass
对象。
非成员函数重载
#include <iostream>
class MyClass {
private:
int value;
public:
MyClass(int v) : value(v) {}
int getValue() const {
return value;
}
friend std::ostream& operator<<(std::ostream& os, const MyClass& obj);
};
std::ostream& operator<<(std::ostream& os, const MyClass& obj) {
os << "Value: " << obj.value;
return os;
}
int main() {
MyClass obj(10);
std::cout << obj << std::endl; // 输出:Value: 10
return 0;
}
在上述示例中,我们将左移运算符(<<)重载为非成员函数。重载函数接受两个参数,第一个参数是要输出到的流对象(如std::cout),第二个参数是要输出的MyClass对象。在函数体内,我们使用流对象将信息输出到标准输出流,并返回流对象本身。
链式编程(Chaining)
是一种编程风格,它允许将多个操作或方法调用连接在一起,形成一个连续的调用链。每个操作都会在原始对象上产生一个修改或转换,并返回修改后的对象,以便可以继续对其进行下一个操作。例如:std::cout << obj << std::endl;
递增运算符重载(++)
通过重载递增运算符,可以定义自定义类在使用递增运算符时的行为。
前置递增运算符(++obj)
前置递增运算符返回递增后的对象引用。它会先递增对象的值,然后返回递增后的对象引用。
MyClass& operator++() {
++value;
return *this;
}
使用前置递增运算符时,对象的值会在表达式求值之前递增,并且递增后的对象会作为结果直接使用。
后置递增运算符(obj++)
后置递增运算符返回递增前的对象副本。它会先创建一个临时副本,然后递增对象的值,最后返回临时副本。
MyClass operator++(int) {
MyClass temp(*this);
++value;
return temp;
}
使用后置递增运算符时,对象的值会在表达式求值后递增,返回的结果是递增前的对象副本。
占位参数
对于后置递增运算符的重载,占位参数
(int)
的作用就是用来区分前置递增和后置递增。在实际使用时,并不需要传递实际的参数值给这个占位参数。占位参数(int)
的目的是为了让编译器能够识别并调用正确版本的递增运算符函数。当编译器看到递增运算符被用在对象之后时,它知道应该调用后置递增运算符函数,并且这个函数的参数列表中应该有一个int
类型的占位参数。因为在后置递增运算符的实现中,我们并不需要使用这个参数的实际值,所以传递任何具体的参数值都是多余的。只要保证参数列表中有一个类型为int
的参数即可。通过这种方式,编译器可以根据调用方式准确地选择对应的递增运算符函数,而无需传递实际的值给占位参数。
赋值运算符重载(=)
赋值运算符(=)用于将一个对象的值或状态复制给另一个对象。通过重载赋值运算符,可以定义自定义类在使用赋值运算符时的行为,从而实现对象之间的成员变量的逐个赋值或深拷贝。
#include <iostream>
class MyClass {
private:
int value;
public:
MyClass(int v = 0) : value(v) {}
int getValue() const {
return value;
}
// 赋值运算符重载
MyClass& operator=(const MyClass& other) {
if (this != &other) {
value = other.value;
}
return *this;
}
};
int main() {
MyClass obj1(5);
MyClass obj2;
std::cout << "obj1 值: " << obj1.getValue() << std::endl;
std::cout << "obj2 值: " << obj2.getValue() << std::endl;
obj2 = obj1; // 使用赋值运算符将 obj1 的值赋给 obj2
std::cout << "赋值后 obj2 值: " << obj2.getValue() << std::endl;
return 0;
}
在上面的示例中,我们定义了一个
MyClass
类,并重载了赋值运算符(=)。赋值运算符接受一个类型相同的对象作为参数,并将其成员变量的值复制给当前对象。在重载函数内部,我们首先检查对象是否与自身相同,以防止自我赋值。然后,我们将参数对象的值复制给当前对象。在main
函数中,我们创建了两个MyClass
对象obj1
和obj2
,并展示了使用赋值运算符将obj1
的值赋给obj2
的过程和结果。
关系运算符重载
关系运算符(比如:==、!=、<、>、<=、>=)用于比较两个对象之间的关系,并返回一个布尔值(true或false)。通过重载关系运算符,你可以定义自定义类在进行比较操作时的行为,从而实现对象之间的自定义比较规则。
#include <iostream>
class MyClass {
private:
int value;
public:
MyClass(int v = 0) : value(v) {}
int getValue() const {
return value;
}
// 相等运算符(==)重载
bool operator==(const MyClass& other) const {
return value == other.value;
}
// 小于运算符(<)重载
bool operator<(const MyClass& other) const {
return value < other.value;
}
};
int main() {
MyClass obj1(5);
MyClass obj2(10);
if (obj1 == obj2) {
std::cout << "obj1 和 obj2 相等" << std::endl;
} else {
std::cout << "obj1 和 obj2 不相等" << std::endl;
}
if (obj1 < obj2) {
std::cout << "obj1 小于 obj2" << std::endl;
} else {
std::cout << "obj1 不小于 obj2" << std::endl;
}
return 0;
}
在上面的示例中,我们定义了一个
MyClass
类,并重载了相等运算符(==)和小于运算符(<)。相等运算符接受一个类型相同的对象作为参数,并比较对象的成员变量是否相等。我们在重载函数中通过比较
value
成员变量的值来实现相等比较。小于运算符同样接受一个类型相同的对象作为参数,并比较对象的成员变量的大小关系。我们在重载函数中通过比较
value
成员变量的值来实现小于比较。
函数调用运算符()重载
函数调用运算符(())可以用于将类对象像函数一样调用,从而实现类的对象实例的可调用行为。
通过重载函数调用运算符,你可以定义自定义类在使用函数调用运算符调用对象时的行为,从而使类对象能够像函数一样执行操作。
函数调用运算符 () 也可以重载
由于重载后使用的方式非常像函数的调用,因此称为仿函数
仿函数没有固定写法,非常灵活
#include <iostream>
class MyClass {
public:
void operator()(int value) const {
std::cout << "调用 MyClass 对象,传入参数: " << value << std::endl;
}
};
int main() {
MyClass obj;
obj(10); // 使用函数调用运算符调用 MyClass 对象
return 0;
}
在上面的示例中,我们定义了一个
MyClass
类,并重载了函数调用运算符(())。通过在类中定义一个名为operator()
的函数成员,我们使得MyClass
对象能够像函数一样被调用。在重载函数中,我们可以实现自定义的操作。在这个示例中,我们简单地打印了传入的参数值。在main
函数中,我们创建了一个MyClass
对象obj
,然后使用函数调用运算符将该对象像函数一样调用,并传递一个整数参数。
仿函数
仿函数又称为函数对象,是一种能够行使函数功能的类,该类重载了operator()运算符,调用仿函数的时候实际上就是通过类对象调用重载后的operator操作符,重载operator()和重载普通的函数效果相同,当参数类型不同时会执行不同的代码逻辑。它可以像函数一样被调用。与普通函数不同,仿函数可以存储状态并具有行为。
#include <iostream>
class Adder {
public:
int operator()(int a, int b) const {
return a + b;
}
};
int main() {
Adder add;
int result = add(5, 3); // 使用仿函数调用
std::cout << "结果: " << result << std::endl;
return 0;
}
在上面的示例中,我们定义了一个名为
Adder
的类,并重载了函数调用运算符(())。通过在类中定义名为operator()
的函数成员,我们将Adder
对象变成了一个仿函数。在重载函数中,我们实现了将两个整数相加的操作。在main
函数中,我们创建了一个Adder
对象add
,然后使用该对象进行函数调用,传递两个整数参数。这样就实现了仿函数的行为,使得add
对象像一个函数一样执行加法操作。