当前位置: 首页 > news >正文

C++ 运算符重载:C++ 运算符重载的高级技巧和最佳实践

C++ 运算符重载:深入剖析与实现

  • I. 引言
    • A. 什么是运算符重载
    • B. 为什么要使用运算符重载
    • C. C++运算符重载的优缺点
  • II. 运算符重载基本概念
    • A. 运算符重载的定义
    • B. 运算符重载的分类
      • 1. 一元运算符
      • 2. 二元运算符
    • C. 限制与规范
      • 1. 无法重载的运算符
      • 2. 重载运算符的规范与建议
  • III. 运算符重载实例
    • A. 重载一元运算符
        • 1. 重载前缀递增/递减运算符
      • 2. 重载后缀递增/递减运算符
      • 3. 重载取反运算符
    • B. 重载二元运算符
      • 1. 重载算术运算符
        • a. 加法运算符
        • b. 减法运算符
        • c. 乘法运算符
        • d. 除法运算符
        • e. 模运算符
      • 2. 重载关系运算符
        • a. 等于运算符
        • b. 不等于运算符
        • c. 大于运算符
        • d. 小于运算符
        • e. 大于等于运算符
        • f. 小于等于运算符
      • 3. 重载赋值运算符
        • a. 重载复合赋值运算符
        • b. 重载移动赋值运算符
      • 4. 重载输入/输出运算符
        • a. 重载流插入运算符
        • b. 重载流提取运算符
  • IV. 运算符重载的实际应用案例
    • A. 运算符重载在类的设计中的作用
    • B. 运算符重载与友元函数
    • C. 运算符重载与类成员函数
  • V. 运算符重载与类的设计
    • A. 复数类的实现
    • B. 向量类的实现
    • C. 矩阵类的实现
    • D. 字符串类的实现
    • E. 分数类的实现
    • F. 二维点类的实现
    • G. 三维向量类的实现
  • Ⅵ 运算符重载在设计模式中的运用
    • A. 代理模式
    • B. 享元模式
    • C. 装饰器模式
  • Ⅶ 运算符重载的底层逻辑
  • Ⅷ 常见问题与解答
    • 问题:运算符重载是否会影响程序性能?
    • 问题:为什么不能重载内置类型的运算符?
    • 问题:成员函数和友元函数在运算符重载中有什么区别?
    • 问题:为什么有些运算符不能重载?
    • 问题:运算符重载是否有助于提高代码可读性?
  • Ⅸ. 总结
    • 常用运算符重载的总结
  • Ⅹ. 实践练习

I. 引言

A. 什么是运算符重载

运算符重载是C++中一种特殊的函数重载机制,它允许我们对已有的运算符赋予新的含义以适应不同数据类型的操作。通过运算符重载,我们可以使用自定义的类或结构体类型进行运算,从而提高代码的可读性和整洁性。运算符重载的实质是编写一个或多个特殊的成员函数或友元函数,这些函数负责处理特定运算符的操作。

B. 为什么要使用运算符重载

运算符重载的主要目的是使自定义数据类型的操作更加直观和易于理解。通过运算符重载,我们可以使用与内置数据类型相同的语法和形式来操作自定义数据类型,从而简化代码并提高代码可读性。此外,运算符重载还可以提高代码的整洁性,使代码更符合数学或其他领域的表达习惯。例如,在实现一个复数类时,通过重载加法运算符,我们可以直接使用 “+” 来表示复数的加法,而不需要调用特定的成员函数。

C. C++运算符重载的优缺点

  • 优点:
  1. 提高代码可读性:运算符重载使得自定义数据类型的操作更加直观,从而提高代码的可读性。

  2. 代码整洁性:通过运算符重载,我们可以简化代码,使其更符合数学或其他领域的表达习惯。

  3. 一致性:运算符重载提供了一种与内置数据类型相同的操作方式,使得自定义数据类型的操作更加一致。

  • 缺点:
  1. 滥用可能导致代码难以理解:如果滥用运算符重载,可能导致代码变得难以理解。重载运算符的含义应该符合预期的逻辑,否则可能引起混淆。

  2. 学习成本:对于初学者来说,理解和掌握运算符重载的概念和用法可能需要一定的学习成本。

  3. 无法重载所有运算符:C++不允许重载某些运算符,例如条件运算符(?:)和逗号运算符(,)。这在某些情况下可能会限制我们的编程灵活性


II. 运算符重载基本概念

A. 运算符重载的定义

运算符重载是通过为已有的运算符定义新的操作来适应不同数据类型的一种机制。运算符重载实质上是在定义一个特殊的函数,这个函数负责处理指定运算符的操作。运算符重载可以通过成员函数或友元函数来实现。

B. 运算符重载的分类

1. 一元运算符

一元运算符是只需要一个操作数的运算符,例如递增(++)、递减(–)和取反(!)。一元运算符可以通过成员函数或友元函数来实现。

2. 二元运算符

二元运算符是需要两个操作数的运算符,例如加法(+)、减法(-)和乘法(*)。二元运算符通常通过成员函数或友元函数来实现。

C. 限制与规范

1. 无法重载的运算符

C++不允许重载以下运算符:

  • 条件运算符(?:)
  • 作用域解析运算符(::)
  • 成员选择运算符(.*)
  • 成员指针选择运算符(->*)
  • 大括号初始化运算符({})
  • 逗号运算符(,)

2. 重载运算符的规范与建议

为了避免混淆和误解,使用运算符重载时需要遵循一定的规范和建议:

  • 重载的运算符应该符合其原始含义和预期行为。
  • 避免过多地重载运算符,以免使代码变得难以理解。
  • 在可能的情况下,为了保持一致性和易用性,重载的运算符应该具有与内置类型相似的优先级和结合性。
  • 使用友元函数进行重载时,应谨慎选择,以防止破坏类的封装性。

III. 运算符重载实例

A. 重载一元运算符

1. 重载前缀递增/递减运算符

//重载前缀递增/递减运算符通常通过成员函数实现,函数没有参数并返回引用。
class Counter {
public:Counter(int value = 0) : value(value) {}Counter& operator++() { // 前缀递增++value;return *this;}Counter& operator--() { // 前缀递减--value;return *this;}private:int value;
};

2. 重载后缀递增/递减运算符

//重载后缀递增/递减运算符也是通过成员函数实现,函数带有一个int类型的参数(不使用),并返回值。
class Counter {
public:Counter(int value = 0) : value(value) {}Counter operator++(int) { // 后缀递增Counter temp = *this;++value;return temp;}Counter operator--(int) { // 后缀递减Counter temp = *this;--value;return temp;}private:int value;
};

3. 重载取反运算符

//重载取反运算符通过成员函数实现,函数没有参数并返回值。
class Boolean {
public:Boolean(bool value = false) : value(value) {}Boolean operator!() const { // 取反return Boolean(!value);}private:bool value;
};

B. 重载二元运算符

1. 重载算术运算符

a. 加法运算符

//重载加法运算符可以通过成员函数或友元函数实现,以下是一个复数类的例子。
class Complex {
public:Complex(double real = 0, double imaginary = 0) : real(real), imaginary(imaginary) {}Complex operator+(const Complex& rhs) const { // 加法return Complex(real + rhs.real, imaginary + rhs.imaginary);}friend Complex operator+(const Complex& lhs, const Complex& rhs) {return Complex(lhs.real + rhs.real, lhs.imaginary + rhs.imaginary);}private:double real;double imaginary;
};

b. 减法运算符

重载减法运算符的实现与加法运算符类似。

c. 乘法运算符

class Complex {
public:Complex(double real = 0, double imaginary = 0) : real(real), imaginary(imaginary) {}Complex operator*(const Complex& rhs) const { // 乘法double newReal = real * rhs.real - imaginary * rhs.imaginary;double newImaginary = real * rhs.imaginary + imaginary * rhs.real;return Complex(newReal, newImaginary);}friend Complex operator*(const Complex& lhs, const Complex& rhs) {double newReal = lhs.real * rhs.real - lhs.imaginary * rhs.imaginary;double newImaginary = lhs.real * rhs.imaginary + lhs.imaginary * rhs.real;return Complex(newReal, newImaginary);}
private:double real;double imaginary;
};

d. 除法运算符

重载除法运算符的实现与乘法运算符类似。

e. 模运算符

重载模运算符的实现通常与加法和减法运算符类似。

2. 重载关系运算符

a. 等于运算符

class String {
public:bool operator==(const String& rhs) const { // 等于return strcmp(data, rhs.data) == 0;}friend bool operator==(const String& lhs, const String& rhs) {return strcmp(lhs.data, rhs.data) == 0;}private:char* data;
};

b. 不等于运算符

重载不等于运算符的实现通常基于等于运算符。

c. 大于运算符

重载大于运算符的实现通常与等于运算符类似。

d. 小于运算符

重载小于运算符的实现通常与等于运算符类似。

e. 大于等于运算符

重载大于等于运算符的实现通常基于大于和等于运算符。

f. 小于等于运算符

重载小于等于运算符的实现通常基于小于和等于运算符。

3. 重载赋值运算符

a. 重载复合赋值运算符

//重载复合赋值运算符通常通过成员函数实现,函数带有一个常量引用参数并返回引用。
class Integer {
public:Integer(int value = 0) : value(value) {}Integer& operator+=(const Integer& rhs) { // 复合加法赋值value += rhs.value;return *this;}private:int value;
};

b. 重载移动赋值运算符

//重载移动赋值运算符通过成员函数实现,函数带有一个右值引用参数并返回引用。
class String {
public:String& operator=(String&& rhs) { // 移动赋值if (this != &rhs) {delete[] data;data = rhs.data;rhs.data = nullptr;}return *this;}private:char* data;
};

4. 重载输入/输出运算符

a. 重载流插入运算符

//重载流插入运算符通常通过友元函数实现,函数带有一个ostream引用参数和一个自定义类型的常量引用参数,并返回ostream引用。
class Point {
public:friend std::ostream& operator<<(std::ostream& os, const Point& point) { // 流插入os << "(" << point.x <<    ", " << point.y << ")";return os;
private:int x;int y;
};  

b. 重载流提取运算符

//重载流提取运算符通常通过友元函数实现,函数带有一个istream引用参数和一个自定义类型的引用参数,并返回istream引用。
class Point {
public:friend std::istream& operator>>(std::istream& is, Point& point) { // 流提取is >> point.x >> point.y;return is;}private:int x;int y;
};

IV. 运算符重载的实际应用案例

在本节中,我们将讨论运算符重载在实际编程中的应用案例,以及它们在类设计中的作用。我们还将讨论运算符重载与友元函数以及类成员函数的关系。

A. 运算符重载在类的设计中的作用

运算符重载在类设计中的主要作用是提供自然、直观的语法来表示自定义数据类型的操作。以下是一些典型的运算符重载应用案例:

  1. 矩阵类:实现矩阵的加法、减法、乘法等操作。
  2. 分数类:实现分数的加法、减法、乘法、除法以及约分等操作。
  3. 复数类:实现复数的加法、减法、乘法、除法等操作。
  4. 字符串类:实现字符串的连接、比较、查找等操作。
  5. 向量类:实现向量的加法、减法、点积、叉积等操作。

B. 运算符重载与友元函数

在实现运算符重载时,我们有时需要在类之外定义运算符函数。这种情况下,我们可以使用友元函数。友元函数可以访问类的私有和保护成员,同时具有全局函数的调用方式。以下是一些典型的运算符重载与友元函数的应用案例:

  1. 输入/输出运算符:通常通过友元函数实现,以便在类之外访问私有成员。
  2. 二元运算符:在需要访问两个不同对象的私有成员时,可以使用友元函数。
  3. 关系运算符:当需要访问两个对象的私有成员进行比较时,可以使用友元函数。

C. 运算符重载与类成员函数

运算符重载可以作为类的成员函数实现,这使得我们可以直接访问类的私有成员。以下是一些典型的运算符重载与类成员函数的应用案例:

  1. 一元运算符:通常通过成员函数实现,因为它们只涉及一个对象的操作。
  2. 赋值运算符:通常通过成员函数实现,因为它们涉及修改对象的内部状态。
  3. 复合赋值运算符:通常通过成员函数实现,因为它们涉及修改对象的内部状态。

在实际应用中,我们可以根据需要选择友元函数或成员函数来实现运算符重载。通过合理地使用运算符重载,我们可以提高代码的可读性和整洁性,使得自定义数据类型的操作更加直观和自然。


V. 运算符重载与类的设计

在本节中,我们将讨论运算符重载在类设计中的应用,以及如何利用运算符重载实现复数类、向量类、矩阵类和字符串类。

A. 复数类的实现

class Complex {
public:Complex(double real = 0.0, double imaginary = 0.0) : real(real), imaginary(imaginary) {}// 加法运算符重载Complex operator+(const Complex& rhs) const {return Complex(real + rhs.real, imaginary + rhs.imaginary);}// 减法运算符重载Complex operator-(const Complex& rhs) const {return Complex(real - rhs.real, imaginary - rhs.imaginary);}// 乘法运算符重载Complex operator*(const Complex& rhs) const {double r = real * rhs.real - imaginary * rhs.imaginary;double i = real * rhs.imaginary + imaginary * rhs.real;return Complex(r, i);}// ... 其他运算符重载private:double real;double imaginary;
};

B. 向量类的实现

class Vector {
public:Vector(double x = 0.0, double y = 0.0) : x(x), y(y) {}// 加法运算符重载Vector operator+(const Vector& rhs) const {return Vector(x + rhs.x, y + rhs.y);}// 减法运算符重载Vector operator-(const Vector& rhs) const {return Vector(x - rhs.x, y - rhs.y);}// 点积运算符重载double operator*(const Vector& rhs) const {return x * rhs.x + y * rhs.y;}// ... 其他运算符重载private:double x;double y;
};

C. 矩阵类的实现

class Matrix {
public:Matrix(int rows, int cols) : rows(rows), cols(cols), data(rows, std::vector<double>(cols, 0.0)) {}// 加法运算符重载Matrix operator+(const Matrix& rhs) const {Matrix result(rows, cols);for (int i = 0; i < rows; ++i) {for (int j = 0; j < cols; ++j) {result.data[i][j] = data[i][j] + rhs.data[i][j];}}return result;}// ... 其他运算符重载private:int rows;int cols;std::vector<std::vector<double>> data;
};

D. 字符串类的实现

class String {
public:String(const char* str = "") : data(str) {}// 加法运算符重载String operator+(const String& rhs) const {return String((data + rhs.data).c_str());}// 赋值运算符重载String& operator=(const String& rhs) {if (this != &rhs) {data = rhs.data;}return *this;}// ... 其他运算符重载private:std::string data;
};

E. 分数类的实现

class Fraction {
public:Fraction(int numerator = 0, int denominator = 1) : numerator(numerator), denominator(denominator) {simplify();}// 加法运算符重载Fraction operator+(const Fraction& rhs) const {int newNumerator = numerator * rhs.denominator + rhs.numerator * denominator;int newDenominator = denominator * rhs.denominator;return Fraction(newNumerator, newDenominator);}// ... 其他运算符重载private:void simplify() {// 约分操作(使用辗转相除法求最大公约数)}int numerator;int denominator;
};

F. 二维点类的实现

class Point2D {
public:Point2D(double x = 0.0, double y = 0.0) : x(x), y(y) {}// 加法运算符重载Point2D operator+(const Point2D& rhs) const {return Point2D(x + rhs.x, y + rhs.y);}// 减法运算符重载Point2D operator-(const Point2D& rhs) const {return Point2D(x - rhs.x, y - rhs.y);}// ... 其他运算符重载private:double x;double y;
};

G. 三维向量类的实现

class Vector3D {
public:Vector3D(double x = 0.0, double y = 0.0, double z = 0.0) : x(x), y(y), z(z) {}// 加法运算符重载Vector3D operator+(const Vector3D& rhs) const {return Vector3D(x + rhs.x, y + rhs.y, z + rhs.z);}// 减法运算符重载Vector3D operator-(const Vector3D& rhs) const {return Vector3D(x - rhs.x, y - rhs.y, z - rhs.z);}// 点积运算符重载double operator*(const Vector3D& rhs) const {return x * rhs.x + y * rhs.y + z * rhs.z;}// 叉积运算符重载Vector3D operator^(const Vector3D& rhs) const {double newX = y * rhs.z - z * rhs.y;double newY = z * rhs.x - x * rhs.z;double newZ = x * rhs.y - y * rhs.x;return Vector3D(newX, newY, newZ);}// ... 其他运算符重载private:double x;double y;double z;
};

Ⅵ 运算符重载在设计模式中的运用

运算符重载在设计模式中的应用不是非常普遍,因为设计模式通常关注更高层次的架构和行为。然而,在某些情况下,运算符重载可以用于简化设计模式的实现。以下是几个示例:

A. 代理模式

代理模式中,代理类通常会代替实际类处理某些操作。在这种情况下,我们可以利用运算符重载来实现代理类与实际类之间的交互。

class RealObject {
public:int value() const { return value_; }void setValue(int value) { value_ = value; }private:int value_;
};class Proxy {
public:Proxy(RealObject* realObject) : realObject_(realObject) {}// 重载->运算符,使代理类可以像实际类一样使用RealObject* operator->() { return realObject_; }private:RealObject* realObject_;
};int main() {RealObject realObj;Proxy proxy(&realObj);proxy->setValue(42);std::cout << "Value: " << proxy->value() << std::endl;return 0;
}

B. 享元模式

享元模式中,我们可以使用运算符重载来实现对共享对象的引用计数。

class SharedObject {
public:SharedObject() : refCount_(0) {}void addRef() { ++refCount_; }void release() {if (--refCount_ == 0) {delete this;}}private:int refCount_;
};class ObjectHandle {
public:ObjectHandle(SharedObject* obj = nullptr) : obj_(obj) {if (obj_) {obj_->addRef();}}ObjectHandle(const ObjectHandle& rhs) : obj_(rhs.obj_) {if (obj_) {obj_->addRef();}}~ObjectHandle() {if (obj_) {obj_->release();}}// 重载赋值运算符,实现引用计数ObjectHandle& operator=(const ObjectHandle& rhs) {if (obj_ != rhs.obj_) {if (obj_) {obj_->release();}obj_ = rhs.obj_;if (obj_) {obj_->addRef();}}return *this;}private:SharedObject* obj_;
};

C. 装饰器模式

装饰器模式中,装饰器类通常会为被装饰类添加额外的功能。在这种情况下,我们可以使用运算符重载来实现装饰器类对被装饰类的功能扩展。

class Component {
public:virtual void operation() const = 0;
};class ConcreteComponent : public Component {
public:void operation() const override {// 实现具体操作}
};class Decorator : public Component {
public:Decorator(Component* component) : component_(component) {}void operation() const override {// 在调用component_->operation()之前或之后添加额外功能component_->operation();}private:Component* component_;
};

Ⅶ 运算符重载的底层逻辑

运算符重载的底层逻辑是通过为运算符提供自定义实现,从而实现用户自定义类型的支持。编译器在处理运算符重载时,会将运算符的调用转换为对应的函数调用。下面我们来具体了解一下编译器在实现运算符重载时的一些基本原理。

当编译器遇到一个运算符表达式时,它会检查操作数的类型。如果操作数是用户自定义类型(如类或结构体)并且对应的运算符重载函数已经定义,编译器会将运算符的调用替换为该运算符重载函数的调用。运算符重载函数可以是类的成员函数或者非成员函数(通常是友元函数)。

以下是一个简单的示例,说明了编译器如何处理运算符重载:

class Complex {
public:Complex(double real = 0.0, double imaginary = 0.0) : real_(real), imaginary_(imaginary) {}Complex operator+(const Complex& rhs) const {return Complex(real_ + rhs.real_, imaginary_ + rhs.imaginary_);}private:double real_;double imaginary_;
};int main() {Complex a(1, 2);Complex b(3, 4);Complex c = a + b; // 编译器将这个表达式转换为:Complex c = a.operator+(b);return 0;
}

在这个例子中,Complex 类有一个成员函数 operator+,用于重载加法运算符。当编译器遇到表达式 a + b 时,它会将这个表达式转换为对应的运算符重载函数调用 a.operator+(b)。

编译器处理运算符重载的基本步骤如下:

  1. 解析表达式,确定操作数的类型和运算符。
  2. 搜索与操作数类型匹配的运算符重载函数。这可能包括成员函数和非成员函数(如友元函数)。
  3. 如果找到了匹配的运算符重载函数,则将运算符调用替换为该函数调用。
  4. 如果没有找到匹配的运算符重载函数,但操作数类型支持隐式类型转换,则尝试进行类型转换并重复步骤 2 和 3。
  5. 如果仍然没有找到匹配的运算符重载函数,则报告编译错误。
    需要注意的是,运算符重载并不改变运算符的优先级或结合性。这些特性在编译期间由编译器根据语言规范处理。运算符重载只是允许我们为用户自定义类型提供自定义的运算符实现。

Ⅷ 常见问题与解答

以下是关于C++运算符重载的一些常见问题及解答,以帮助您进一步巩固和深化对运算符重载的理解。

问题:运算符重载是否会影响程序性能?

答:运算符重载通常不会对程序性能产生显著影响。事实上,由于运算符重载是在编译时解析的,因此它不会引入额外的运行时开销。然而,不恰当地使用运算符重载可能导致代码变得难以理解和维护,所以我们需要权衡可读性和性能之间的平衡。

问题:为什么不能重载内置类型的运算符?

答:C++不允许为内置类型重载运算符,因为这可能导致代码混乱和意料之外的行为。运算符重载的主要目的是提供一种方式来定义自定义数据类型的操作,而不是改变内置类型的行为。

问题:成员函数和友元函数在运算符重载中有什么区别?

答:成员函数和友元函数在运算符重载中的主要区别在于访问权限和调用方式。成员函数可以直接访问类的私有成员,而友元函数需要类的显式授权。此外,成员函数的调用方式是基于对象的,而友元函数的调用方式是基于参数的。通常情况下,我们可以根据需要选择成员函数或友元函数进行运算符重载。

问题:为什么有些运算符不能重载?

答:C++不允许重载某些运算符,主要是因为这些运算符具有特定的语义,重载它们可能导致代码混乱和意料之外的行为。例如,条件运算符(?:)和作用域解析运算符(::)具有特定的语法结构和语义,重载它们将破坏语言的一致性。

问题:运算符重载是否有助于提高代码可读性?

答:运算符重载可以提高代码可读性,因为它允许我们使用自然的数学符号表示自定义数据类型的操作。然而,不恰当地使用运算符重载可能导致代码变得难以理解和维护。因此,在使用运算符重载时,我们需要遵循相关规范和建议,确保代码可读性和可维护性。


Ⅸ. 总结

C++中的运算符重载提供了一种灵活的方法来定义自定义数据类型的操作。通过合理地使用运算符重载,我们可以提高代码的可读性和整洁性。然而,运算符重载也有其局限性和需要注意的规范。在实际开发中,我们需要根据实际需求和项目特点来判断是否需要使用运算符重载,以及如何合理地使用运算符重载。总的来说,运算符重载是C++编程的一个重要特性,掌握和运用好运算符重载,可以提高我们编程的效率和代码质量。

常用运算符重载的总结

以下是一些常用运算符重载的简要总结,帮助您快速回顾和理解运算符重载的应用。

一元运算符

  • 前缀递增/递减:通过成员函数实现,无参数,返回引用。
  • 后缀递增/递减:通过成员函数实现,带一个int类型参数(不使用),返回值。
  • 取反:通过成员函数实现,无参数,返回值。
    二元运算符
  • 算术运算符:可以通过成员函数或友元函数实现,带一个常量引用参数,返回值。
  • 关系运算符:可以通过成员函数或友元函数实现,带一个常量引用参数,返回布尔值。
  • 赋值运算符:通常通过成员函数实现,带一个常量引用参数或右值引用参数,返回引用。
    输入/输出运算符
  • 流插入运算符:通过友元函数实现,带一个ostream引用参数和一个自定义类型的常量引用参数,返回ostream引用。
  • 流提取运算符:通过友元函数实现,带一个istream引用参数和一个自定义类型的引用参数,返回istream引用。
    在使用运算符重载时,请注意遵循相关的规范和建议,避免滥用运算符重载,确保代码可读性和可维护性。

通过以上内容的学习,您应该对C++运算符重载有了较为全面的了解。在实际编程过程中,不断练习和思考,加深对运算符重载的理解和运用,将有助于您更好地利用C++的强大功能,提高编程效率和代码质量。


Ⅹ. 实践练习

为了帮助您更好地掌握C++运算符重载,我们为您准备了一些实践练习。通过完成这些练习,您将巩固对运算符重载的理解,并提高编程能力。

  • 为一个表示分数的类实现加法、减法、乘法和除法运算符。此外,实现关系运算符(如等于、不等于、大于、小于等)以及输入/输出运算符。

  • 实现一个矩阵类,支持加法、减法、乘法和转置等操作。同时实现关系运算符和输入/输出运算符。

  • 为一个表示复数的类实现算术运算符、关系运算符和输入/输出运算符。确保复数的实部和虚部都能正确处理。

  • 实现一个简单的字符串类,支持拼接(+)、比较(==、!=、>、<等)和赋值(=)等操作。同时实现输入/输出运算符。

  • 实现一个表示二维点的类,支持加法(向量相加)、减法(向量相减)、点积(内积)、叉积(外积)以及关系运算符和输入/输出运算符。


在完成这些练习时,请注意遵循C++运算符重载的相关规范和建议。通过实际操作,您将更加深入地了解C++运算符重载的原理和应用,从而提高编程效率和代码质量。

相关文章:

C++ 运算符重载:C++ 运算符重载的高级技巧和最佳实践

C 运算符重载&#xff1a;深入剖析与实现I. 引言A. 什么是运算符重载B. 为什么要使用运算符重载C. C运算符重载的优缺点II. 运算符重载基本概念A. 运算符重载的定义B. 运算符重载的分类1. 一元运算符2. 二元运算符C. 限制与规范1. 无法重载的运算符2. 重载运算符的规范与建议II…...

软件测试找了2个月了,找不到工作怎么办?

那就问你一些问题&#xff0c;看你能回答多少 1:测试流程是什么&#xff1f;测试用例包含哪些内容&#xff1f;测试用例设计都有哪些&#xff1f;给你一个一次性杯子&#xff0c;你会怎么测试&#xff1f; 2:数据库怎么查看前十行数据&#xff1f;内连接和外连接的区别&#…...

满足高并发的TB API接口接入说明

大家都知道&#xff0c;淘宝的反爬虫机制十分严&#xff0c;而很多时候&#xff0c;没办法高效的拿到数据内容响应终端需求&#xff0c;而依赖爬虫就会造成动不动就出现滑块验证&#xff0c;让人很无解。这里我们分享让采集不再出现任何滑块验证码&#xff0c;完全解密通过&…...

Themis Pro版将正式推出,3次迭代到底在酝酿什么?

最近在社区内讨论火热的Themis Pro&#xff0c;终于要来了&#xff01;4月2日Themis官网&#xff08;themis.capital &#xff09;全新升级改版上线&#xff0c;并宣布Themis Pro 即将于4月下旬正式推出。 Themis Pro 是基于Ve(3,3&#xff09;模型在FVM公链上搭建的新一代去中…...

边缘检测和轮廓检测

边缘检测 什么是边缘: * 图像中像素值发生剧烈变化的位置(高频信息区域) * 这些区域往往都是图像的边缘 方法:滤波、形态学处理等 边缘的作用 本质上,边缘是不同区域之间的边界。 其中包含了图像的区域信息,形状信息 一方面,可以利用这些信息来作为特征对图像进行理解(甚至…...

二分法模板以及例题 (三)

167. 两数之和 II - 输入有序数组 输入&#xff1a;numbers [2,7,11,15], target 9 输出&#xff1a;[1,2]。 解释&#xff1a;2 与 7 之和等于目标数 9 。因此 index1 1, index2 2 。返回 [1, 2] 解题思路&#xff1a;首先散列表可以直接秒了&#xff0c;双指针也秒了 二分…...

向下转型和向上转型(易理解)

向上转型&#xff1a;父类引用指向子类对象 定义A B C D 四个类&#xff0c;分级继承 对象 a 的编译类型是A&#xff0c;运行类型是B&#xff0c;A是B的父类&#xff0c;父类的引用 a 指向的是B这个子类的对象&#xff0c;因为new的是B这个类&#xff0c;创建的也就是B这个类的…...

华为OD机试用JS实现 -【机智的外卖员】(2023-Q2 押题)

最近更新的博客 华为od 2023 | 什么是华为od,od 薪资待遇,od机试题清单华为OD机试真题大全,用 Python 解华为机试题 | 机试宝典【华为OD机试】全流程解析+经验分享,题型分享,防作弊指南华为od机试,独家整理 已参加机试人员的实战技巧本篇题解:机智的外卖员 题目描述: 外…...

同态加密:一个基于多方计算的CKKS方案

这篇文章主要介绍LattiGo团队搞出来的一个多方同态加密的工作。个人觉得比较优雅&#xff0c;而且有库支持&#xff0c;方便把玩&#xff0c;所以记一下。 在攒毕业论文的时候整了这么个看上去很烂&#xff0c;但是&#xff08;个人觉得&#xff09;有一点意思的烂活&#xff0…...

最小生成数

题目描述 如题&#xff0c;给出一个无向图&#xff0c;求出最小生成树&#xff0c;如果该图不连通&#xff0c;则输出 orz。 输入格式 第一行包含两个整数 &#xfffd;,&#xfffd;N,M&#xff0c;表示该图共有 &#xfffd;N 个结点和 &#xfffd;M 条无向边。 接下来 …...

【模板】树状数组

目录&#xff1a; 单点修改&#xff0c;区间查询&#xff1a; 题目描述&#xff1a; lowbit()运算&#xff1a; 插入、修改单点数据&#xff1a; 计算前缀和&#xff1a; 完整代码&#xff1a; 区间修改&#xff0c;单点查询&#xff1a; 计算差分数组&#xff1a; 计算每个点的…...

网站都变成灰色了,怎么实现的?

有些时候我们需要把网站页面变成黑白色或灰色&#xff0c;特别是对于一些需要悼念的日子&#xff0c;以及一些影响力很大的伟人逝世或纪念日的时候&#xff0c;都会让网站的全部网页变成灰色&#xff08;黑白色&#xff09;&#xff0c;以表示我们对逝者或者英雄的缅怀和悼念。…...

NeRF详解

论文标题&#xff1a;《NeRF: Representing Scenes as Neural Radiance Fields for View Synthesis》 论文地址&#xff1a;https://arxiv.org/abs/2003.08934 推荐代码&#xff1a;https://github.com/yenchenlin/nerf-pytorch 文章目录前言隐式表达NeRF的训练位置编码体渲染&…...

Java之静态代码块和静态类、静态导入

前言 在上一篇文章中给大家讲解了static静态关键字&#xff0c;以及静态变量、静态常量和静态方法等内容。但是关于static&#xff0c;还有其他的一些内容&#xff0c;比如静态类、静态代码块和静态导入等&#xff0c;接下来给大家继续分析讲解。我们一起来看看这些内容都是怎…...

Python3 File isatty() 、os.chflags()方法

Python3 File isatty() 方法Python3 File(文件) 方法概述isatty() 方法检测文件是否连接到一个终端设备&#xff0c;如果是返回 True&#xff0c;否则返回 False。语法isatty() 方法语法如下&#xff1a;fileObject.isatty(); 参数无返回值如果连接到一个终端设备返回 True&…...

【SH_CO_TMT_PACKAGE保留60天数据和增加索引】

1. 保留60天数据 DELETE FROM SH_CO_TMT_PACKAGE WHERE CREATED_ < SYSDATE - 195SH_CO_TMT_PACKAGE 这个表是tmt的数据统计表,数据量极大,大概有1500W 里头的数据都是从TMT机台运行状况表(比如满管率,断丝数,下次落纱时间)同步过来的。 朗通针对这些数据,做了个…...

2022蓝桥杯省赛——数位排序

问题描述 小蓝对一个数的数位之和很感兴趣, 今天他要按照数位之和给数排序。当两个数各个数位之和不同时, 将数位和较小的排在前面, 当数位之和相等时, 将数值小的排在前面。 例如, 2022 排在 409 前面, 因为 2022 的数位之和是 6, 小于 409 的数位之和 13 。 又如, 6 排在 …...

弥散磁共振成像在神经科学中的应用

导读弥散加权成像技术突破了神经科学的界限&#xff0c;使我们能够检查活体人脑的白质微观结构。这为基本的神经科学问题提供了答案&#xff0c;开启了一个以前基本上难以接近的新研究领域。本研究简要总结了神经科学历史上提出的关于大脑白质的关键问题。然后&#xff0c;阐述…...

多进程(python)

参考&#xff1a; https://www.liaoxuefeng.com/wiki/1016959663602400/1017627212385376 个人封装的python多进程处理类&#xff0c;跑满CPU&#xff0c;优化性能 概念 进程: 对于操作系统来说&#xff0c;一个任务就是一个进程&#xff08;Process&#xff09;&#xff0c;…...

利用Kali工具进行信息收集(35)

预备知识 Kali是一款开源的安全漏洞检测工具&#xff0c;是专业用于渗透测试的Linux操作系统&#xff0c;由BackTrack发展而来&#xff0c;可以帮助安全和IT专业人士识别安全性问题&#xff0c;验证漏洞的缓解措施&#xff0c;并管理专家驱动的安全性进行评估&#xff0c;提供真…...

Qwen3-Embedding-0.6B深度解析:多语言语义检索的轻量级利器

第一章 引言&#xff1a;语义表示的新时代挑战与Qwen3的破局之路 1.1 文本嵌入的核心价值与技术演进 在人工智能领域&#xff0c;文本嵌入技术如同连接自然语言与机器理解的“神经突触”——它将人类语言转化为计算机可计算的语义向量&#xff0c;支撑着搜索引擎、推荐系统、…...

【Java_EE】Spring MVC

目录 Spring Web MVC ​编辑注解 RestController RequestMapping RequestParam RequestParam RequestBody PathVariable RequestPart 参数传递 注意事项 ​编辑参数重命名 RequestParam ​编辑​编辑传递集合 RequestParam 传递JSON数据 ​编辑RequestBody ​…...

OpenLayers 分屏对比(地图联动)

注&#xff1a;当前使用的是 ol 5.3.0 版本&#xff0c;天地图使用的key请到天地图官网申请&#xff0c;并替换为自己的key 地图分屏对比在WebGIS开发中是很常见的功能&#xff0c;和卷帘图层不一样的是&#xff0c;分屏对比是在各个地图中添加相同或者不同的图层进行对比查看。…...

解析奥地利 XARION激光超声检测系统:无膜光学麦克风 + 无耦合剂的技术协同优势及多元应用

在工业制造领域&#xff0c;无损检测&#xff08;NDT)的精度与效率直接影响产品质量与生产安全。奥地利 XARION开发的激光超声精密检测系统&#xff0c;以非接触式光学麦克风技术为核心&#xff0c;打破传统检测瓶颈&#xff0c;为半导体、航空航天、汽车制造等行业提供了高灵敏…...

Qt 事件处理中 return 的深入解析

Qt 事件处理中 return 的深入解析 在 Qt 事件处理中&#xff0c;return 语句的使用是另一个关键概念&#xff0c;它与 event->accept()/event->ignore() 密切相关但作用不同。让我们详细分析一下它们之间的关系和工作原理。 核心区别&#xff1a;不同层级的事件处理 方…...

MFE(微前端) Module Federation:Webpack.config.js文件中每个属性的含义解释

以Module Federation 插件详为例&#xff0c;Webpack.config.js它可能的配置和含义如下&#xff1a; 前言 Module Federation 的Webpack.config.js核心配置包括&#xff1a; name filename&#xff08;定义应用标识&#xff09; remotes&#xff08;引用远程模块&#xff0…...

Python训练营-Day26-函数专题1:函数定义与参数

题目1&#xff1a;计算圆的面积 任务&#xff1a; 编写一个名为 calculate_circle_area 的函数&#xff0c;该函数接收圆的半径 radius 作为参数&#xff0c;并返回圆的面积。圆的面积 π * radius (可以使用 math.pi 作为 π 的值)要求&#xff1a;函数接收一个位置参数 radi…...

uni-app学习笔记三十五--扩展组件的安装和使用

由于内置组件不能满足日常开发需要&#xff0c;uniapp官方也提供了众多的扩展组件供我们使用。由于不是内置组件&#xff0c;需要安装才能使用。 一、安装扩展插件 安装方法&#xff1a; 1.访问uniapp官方文档组件部分&#xff1a;组件使用的入门教程 | uni-app官网 点击左侧…...

node.js的初步学习

那什么是node.js呢&#xff1f; 和JavaScript又是什么关系呢&#xff1f; node.js 提供了 JavaScript的运行环境。当JavaScript作为后端开发语言来说&#xff0c; 需要在node.js的环境上进行当JavaScript作为前端开发语言来说&#xff0c;需要在浏览器的环境上进行 Node.js 可…...

qt+vs Generated File下的moc_和ui_文件丢失导致 error LNK2001

qt 5.9.7 vs2013 qt add-in 2.3.2 起因是添加一个新的控件类&#xff0c;直接把源文件拖进VS的项目里&#xff0c;然后VS卡住十秒&#xff0c;然后编译就报一堆 error LNK2001 一看项目的Generated Files下的moc_和ui_文件丢失了一部分&#xff0c;导致编译的时候找不到了。因…...