当前位置: 首页 > 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;提供真…...

《程序员面试金典(第6版)》 面试题 08.11. 硬币(动态规划,组合问题,C++)

题目描述 硬币。给定数量不限的硬币&#xff0c;币值为25分、10分、5分和1分&#xff0c;编写代码计算n分有几种表示法。(结果可能会很大&#xff0c;你需要将结果模上1000000007) 示例1: 输入: n 5 输出&#xff1a;2 解释: 有两种方式可以凑成总金额: 55 511111 示例2: 输…...

实体商家做抖音运营如何做矩阵?

商家实体门店如何做好短视频矩阵&#xff1f;这是一个值得深入探讨的问题。在当今的数字化时代&#xff0c;短视频成为越来越多企业吸引用户、提高曝光度的一种重要方式&#xff0c;实体店也不例外。在本文中&#xff0c;我们将提供一些实用的建议&#xff0c;帮助实体店如何做…...

java 双列集合Map 万字详解

目录 一、前言 二、概述 三、特点 四、常用方法 1. V put(K key, V value) : Δ代码演示 : 2. V get(Object key) : Δ代码演示 : 3. V remove(Object key) : Δ代码演示 : 4. int size() : Δ代码演示 : 5. default V replace(K key, V value) : Δ代码演示 : 6. bo…...

【数据结构】二叉树<遍历>

【二叉树遍历】|-前序-中序-后序-层序-|<二叉树的遍历>1.前序遍历【递归】2.中序遍历【递归】3.后序遍历【递归】4.层序遍历【非递归】4.1判断是否是完全二叉树<二叉树的遍历> 在学习二叉树遍历之前我们先了解下二叉树的概念。 二叉树是&#xff1a; 1.空树 2.非空…...

linux查看硬件信息

dmidecode用于在linux下获取硬件信息&#xff0c;遵循SMBIOS/DMI标准&#xff0c;可获取包括BIOS、系统、主板、处理器、内存、缓存等等硬件信息 1、查看CPU信息cat /proc/cpuinfo、lscpu 型号&#xff1a;cat /proc/cpuinfo|grep name|cut -f2 -d:|uniq -c 物理核&#xff1a…...

吐血整理,互联网大厂最常见的 1120 道 Java 面试题(带答案)整理

前言 作为一个 Java 程序员&#xff0c;你平时总是陷在业务开发里&#xff0c;每天噼里啪啦忙敲着代码&#xff0c;上到系统开发&#xff0c;下到 Bug 修改&#xff0c;你感觉自己无所不能。然而偶尔的一次聚会&#xff0c;你听说和自己一起出道的同学早已经年薪 50 万&#x…...

RabbitMQ如何避免消息丢失

目录1.生产者没有成功把消息发送到MQ2.RabbitMQ接收到消息之后丢失了消息3.消费者弄丢了消息前言 首先明确一点一条消息的传送流程&#xff1a;生产者->MQ->消费者 我们根据这三个依次讨论 1.生产者没有成功把消息发送到MQ 丢失的原因&#xff1a;因为网络传输的不稳定…...

做算法题的正确姿势(不断更新)

不停的反思自己&#xff0c;总结建议 做一道算法题&#xff0c;不能去死磕。 如果看一道题&#xff0c;半小时内&#xff0c;没有清晰的思路&#xff0c;就看题解&#xff01;&#xff01;&#xff01;你可能觉得你有点思路&#xff0c;就往里死钻&#xff0c;结果可能就像进…...

p85 CTF夺旗-JAVA考点反编译XXE反序列化

数据来源 图片来源 Java 常考点及出题思路 考点技术&#xff1a;xxe&#xff0c;spel 表达式&#xff0c;反序列化&#xff0c;文件安全&#xff0c;最新框架插件漏洞等 设法间接给出源码或相关配置提示文件&#xff0c;间接性源码或直接源码体现等形式 https://www.cnblog…...

FastJson——JSO字符串与对象的相互转化

一、FastJson介绍 ​ Fastjson是阿里巴巴的开源SON解析库它可以解析JSON格式的字符串&#xff0c;支持将java Bean序列化为ISON字符串&#xff0c;也可以从JSON字符串反序列化到JavaBean。 Fastjson的优点 速度快 fastjson相对其他JSON库的特点是快&#xff0c;从2011年fastj…...