[C++核心编程-08]----C++类和对象之运算符重载

🎩 欢迎来到技术探索的奇幻世界👨‍💻

📜 个人主页:@一伦明悦-CSDN博客

✍🏻 作者简介: C++软件开发、Python机器学习爱好者

🗣️ 互动与支持:💬评论      👍🏻点赞      📂收藏     👀关注+

如果文章有所帮助,欢迎留下您宝贵的评论,点赞加收藏支持我,点击关注,一起进步!

 前言       

        在C++中,运算符重载是一种允许你重新定义标准操作符(如+、-、*、/等)的特性,以便它们适用于自定义的数据类型。这种功能使得你可以像操作内置类型一样自然地操作自定义类型的对象。运算符重载是面向对象编程的一个重要特性,它增强了代码的可读性和可维护性。

正文       

01-运算符简介        

        运算符重载允许你对类的对象使用C++内置的运算符进行操作。例如,你可以重载+运算符来实现两个对象的相加,或者重载*运算符来实现对象的乘法操作。这样一来,你就可以像处理基本数据类型一样处理自定义的数据类型。

        在C++中,你可以通过重载类的成员函数或全局函数来实现运算符重载。如果你选择在类中重载运算符,那么运算符函数将作为类的成员函数来定义。如果你选择在类外部重载运算符,那么你需要使用全局函数的形式来定义运算符函数。

        在C++中,你可以通过重载成员函数或全局函数来实现运算符重载。以下是一个简单的示例,说明如何重载+运算符来实现两个复数对象的相加:

        在上示例中,我们创建了一个名为Complex的类来表示复数。我们重载了+运算符,使其能够将两个Complex对象相加。然后,在主函数中,我们创建了两个Complex对象并使用重载的+运算符将它们相加,最后打印了结果。

#include <iostream>

class Complex {
private:
    double real;
    double imaginary;

public:
    Complex(double r = 0.0, double i = 0.0) : real(r), imaginary(i) {}

    // 重载+运算符
    Complex operator+(const Complex& other) const {
        return Complex(real + other.real, imaginary + other.imaginary);
    }

    // 打印复数
    void print() const {
        std::cout << "(" << real << " + " << imaginary << "i)" << std::endl;
    }
};

int main() {
    Complex c1(2.0, 3.0);
    Complex c2(1.0, 2.0);
    
    Complex result = c1 + c2; // 使用重载的+运算符

    std::cout << "Result of addition: ";
    result.print();

    return 0;
}

02-加号运算符重载        

        当重载加号运算符(+)时,可以定义在两个对象之间的相加操作。这样,可以使用加号运算符直接对两个对象进行相加,而不必调用一个特定的成员函数来执行这个操作。这种操作增加了代码的可读性和简洁性。

        在C++中,可以通过在类中或类外部定义特定的成员函数或全局函数来重载加号运算符。下面分别介绍在类中和类外部重载加号运算符的方法。

        在类中重载加号运算符: 在这里,operator+是加号运算符的重载函数,returnType是运算符函数的返回类型,const ClassName& other是另一个对象的引用,表示要与当前对象相加的对象。

class ClassName {
public:
    returnType operator+(const ClassName& other) {
        // 重载的加号运算符实现
    }
};

        下面给出具体例子,在这个例子中,我们在Complex类中重载了加号运算符。在重载函数中,我们定义了两个Complex对象相加的操作,并返回相加后的结果。

#include <iostream>

class Complex {
private:
    double real;
    double imaginary;

public:
    Complex(double r = 0.0, double i = 0.0) : real(r), imaginary(i) {}

    // 重载+运算符
    Complex operator+(const Complex& other) {
        return Complex(real + other.real, imaginary + other.imaginary);
    }

    // 打印复数
    void print() const {
        std::cout << "(" << real << " + " << imaginary << "i)" << std::endl;
    }
};

int main() {
    Complex c1(2.0, 3.0);
    Complex c2(1.0, 2.0);
    
    Complex result = c1 + c2; // 使用重载的+运算符

    std::cout << "Result of addition: ";
    result.print();

    return 0;
}

        在类外部重载加号运算符:在这里,operator+是加号运算符的重载函数,returnType是运算符函数的返回类型,const ClassName& obj1const ClassName& obj2是要相加的两个对象的引用。

returnType operator+(const ClassName& obj1, const ClassName& obj2) {
    // 重载的加号运算符实现
}

         类外部重载加号运算符的例子:在这个例子中,我们在Complex类外部重载了加号运算符。在重载函数中,我们定义了两个Complex对象相加的操作,并返回相加后的结果。

#include <iostream>

class Complex {
private:
    double real;
    double imaginary;

public:
    Complex(double r = 0.0, double i = 0.0) : real(r), imaginary(i) {}

    // 打印复数
    void print() const {
        std::cout << "(" << real << " + " << imaginary << "i)" << std::endl;
    }
};

// 在类外部重载+运算符
Complex operator+(const Complex& c1, const Complex& c2) {
    return Complex(c1.real + c2.real, c1.imaginary + c2.imaginary);
}

int main() {
    Complex c1(2.0, 3.0);
    Complex c2(1.0, 2.0);
    
    Complex result = c1 + c2; // 使用重载的+运算符

    std::cout << "Result of addition: ";
    result.print();

    return 0;
}

        下面给出具体代码分析应用过程,代码演示了如何在C++中进行运算符重载,特别是加号运算符(+)。逐步解释:

         首先,定义了一个名为 Person 的类,其中有两个公有成员变量 m_A 和 m_B,代表人的某些属性。

        接着,通过两种不同的方式重载了加号运算符:

        通过成员函数重载:注释掉的部分代码是使用成员函数重载加号运算符的示例。这种方式是在类内定义一个成员函数,该函数接受一个对象作为参数,并执行加法操作。

        通过全局函数重载:实际使用的方式是在类外定义一个全局函数,该函数接受两个对象作为参数,并执行加法操作。

        另外,还重载了一个全局函数,使得 Person 类型对象可以与整数进行相加操作。

  test01() 函数用于测试加号运算符重载的效果。在函数内部,创建了两个 Person 类型对象 p1 和 p2,并设置它们的属性值。        

        然后,分别演示了两种方式重载加号运算符的调用方式:

        使用成员函数重载加号运算符时,可以像调用普通成员函数一样使用 . 运算符进行调用,即 p1.operator+(p2)

        使用全局函数重载加号运算符时,直接使用加号进行调用,即 p1 + p2

        最后,演示了使用全局函数重载加号运算符将 Person 对象与整数相加的情况,即 p1 + 100

#include <iostream>
using namespace std;

// 加号运算符重载
class Person
{
public: 

	// 1、实现成员函数重载+号    成员函数就是在类里定义的函数,可以少传入一个参数

// 	Person operator+(Person &p)  // operator+这个就是编译器自带的一个函数名称,使用此名称,后面才能简化,其他代码自己写
// 	{
// 		Person temp;
// 		temp.m_A = this->m_A + p.m_A;
// 		temp.m_B = this->m_B + p.m_B;
// 		return temp;
// 
// 	}

	int m_A;
	int m_B;
};

// 2、实现全局函数重载+号    全局函数就是在类外定义的函数,需要同时传入所有的所需参数
Person operator+(Person &p1, Person &p2)
{
	Person temp;
	// 因为m_A是公有的成员变量,因此可以调用
	temp.m_A = p1.m_A + p2.m_A;
	temp.m_B = p1.m_B + p2.m_B;
	return temp;
}
Person operator+(Person &p1, int num)   // 这里采用引用方式,可以防止值传递的时候再进行拷贝一份数据,用不用都可以
{
	Person temp;
	// 因为m_A是公有的成员变量,因此可以调用
	temp.m_A = p1.m_A + num;
	temp.m_B = p1.m_B + num;
	return temp;
}

void test01()
{
	Person p1;
	p1.m_A = 10;
	p1.m_B = 10;
	Person p2;
	p2.m_A = 10;
	p2.m_B = 10;
	// 成员函数重载本质调用
//	Person p3 = p1.operator+(p2);
	//全局函数重载本质调用
//	Person p3 = operator+(p1,p2);

	Person p3 = p1 + p2;   // 这句代码其实是由 Person p3 = p1.operator+(p2);简化而来

	// 运算符重载,也可以发生函数重载,比如如果需要Person类型加上一个整型数据
	// 相当于 Person p4 = operator+(p1, num);
	Person p4 = p1 + 100;


	cout << "p3.m_A = " << p3.m_A << endl;
	cout << "p3.m_B = " << p3.m_B << endl;

	cout << "p4.m_A = " << p4.m_A << endl;
	cout << "p4.m_B = " << p4.m_B << endl;
}

int main()
{
	test01();

	system("pause");
	return 0;
}

        示例运行结果如下图所示:

03-左移运算符重载        

        当重载左移运算符(<<)时,可以定义在自定义类对象上执行左移操作的行为。这使得可以像使用标准输出流对象一样使用左移运算符来输出自定义对象的信息。这种操作可以增强代码的可读性和简洁性。

        在C++中,可以通过在类内部或类外部定义特定的成员函数或全局函数来重载左移运算符。以下是分别在类内部和类外部重载左移运算符的方法。

        在类内部重载左移运算符:operator<< 是左移运算符的重载函数,std::ostream& os 是输出流对象的引用,const ClassName& obj 是要输出的对象的引用。

class ClassName {
public:
    friend std::ostream& operator<<(std::ostream& os, const ClassName& obj) {
        // 重载的左移运算符实现
        os << obj.member1 << " " << obj.member2; // 假设输出对象的成员变量为 member1 和 member2
        return os;
    }
};

        实现在类中重载左移运算符的例子:在这个例子中,我们在 Person 类中重载了左移运算符。在重载函数中,我们定义了如何输出 Person 对象的信息,并使用 std::ostream 对象来进行输出。

#include <iostream>

class Person {
    friend std::ostream& operator<<(std::ostream& os, const Person& person) {
        os << "Name: " << person.name << ", Age: " << person.age;
        return os;
    }

private:
    std::string name;
    int age;

public:
    Person(const std::string& n, int a) : name(n), age(a) {}
};

int main() {
    Person p("John", 30);
    std::cout << p << std::endl; // 使用重载的<<运算符输出对象信息

    return 0;
}

        在类外部重载左移运算符:operator<< 是左移运算符的重载函数,std::ostream& os 是输出流对象的引用,const ClassName& obj 是要输出的对象的引用。

std::ostream& operator<<(std::ostream& os, const ClassName& obj) {
    // 重载的左移运算符实现
    os << obj.member1 << " " << obj.member2; // 假设输出对象的成员变量为 member1 和 member2
    return os;
}

        实现在类外部重载左移运算符的例子:在这个例子中,我们在类外部重载了左移运算符。在重载函数中,我们定义了如何输出 Person 对象的信息,并使用 std::ostream 对象来进行输出。

#include <iostream>

class Person {
    friend std::ostream& operator<<(std::ostream& os, const Person& person);

private:
    std::string name;
    int age;

public:
    Person(const std::string& n, int a) : name(n), age(a) {}
};

std::ostream& operator<<(std::ostream& os, const Person& person) {
    os << "Name: " << person.name << ", Age: " << person.age;
    return os;
}

int main() {
    Person p("John", 30);
    std::cout << p << std::endl; // 使用重载的<<运算符输出对象信息

    return 0;
}

        下面给出具体代码分析应用过程,这段代码演示了如何在C++中进行左移运算符(<<)的重载,以便能够像使用标准输出流对象一样输出自定义类的对象信息。逐步解释:

        首先,定义了一个名为 Person 的类,其中有两个公有成员变量 m_A 和 m_B,代表人的某些属性。

        接着,通过两种不同的方式尝试重载左移运算符:

        通过成员函数重载(注释掉的部分):在成员函数中尝试重载左移运算符,但在C++中,左移运算符 << 的重载必须是全局函数或类的友元函数,不能以成员函数的形式重载。

        通过全局函数重载左移运算符:在类外定义了一个全局函数,该函数接受一个输出流对象 ostream 和一个 Person 对象的引用,并输出该对象的信息。

  test01() 函数用于测试重载的左移运算符的效果。在函数内部,创建了一个 Person 类型对象 p,并设置了其属性值。

        在 main() 函数中,调用了 test01() 函数,并输出了 p 对象的信息。

#include <iostream>
using namespace std;

// 左移运算符重载   和加号一样,两种重载方式

class Person
{
public:
	// 1、成员函数重载
// 	void operator << (ostream &cout , Person &p)
// 	{
// 		cout << "m_A = " << p.m_A << " m_B = " << p.m_B;
// 	}

	int m_A;
	int m_B;
};

// 2、只能利用全局函数重载左移运算符
ostream & operator << (ostream &cout, Person &p)
{
	cout << "m_A = " << p.m_A << " m_B = " << p.m_B;
	return cout;
}

void test01()
{
	Person p;
	p.m_A = 10;
	p.m_B = 10;
	cout << p << endl;

}

int main()
{
	test01();
	system("pause");
	return 0;
}

         示例运行结果如下图所示:

04-递增运算符重载       

        当重载递增运算符(++)时,可以定义在自定义类对象上执行递增操作的行为。递增运算符通常有两种形式:前置递增和后置递增。前置递增返回递增后的对象的引用,而后置递增返回递增前的对象的副本。这使得可以像使用内置数据类型一样使用递增运算符来操作自定义对象。

        在C++中,可以通过在类内部或类外部定义特定的成员函数或全局函数来重载递增运算符。以下是分别在类内部和类外部重载前置递增运算符和后置递增运算符的方法。

        在类内部重载递增运算符:在这里,operator++ 是递增运算符的重载函数。对于前置递增,它返回一个引用,因为递增后的对象可能会在表达式中继续使用。对于后置递增,它返回递增前的对象的副本,因为它需要返回原始值而不是递增后的值。

class ClassName {
public:
    ClassName& operator++() {
        // 实现前置递增操作
        // 修改对象的状态
        return *this; // 返回递增后的对象的引用
    }

    ClassName operator++(int) {
        // 实现后置递增操作
        // 先保存递增前的对象副本
        // 修改对象的状态
        return copy; // 返回递增前的对象的副本
    }
};

        实现在类中重载前置递增运算符和后置递增运算符的例子:在这个例子中,定义了一个 Counter 类,其中包含一个 count 成员变量。在类中重载了前置递增运算符和后置递增运算符,通过增加 count 的值来模拟递增操作。

#include <iostream>

class Counter {
private:
    int count;

public:
    Counter(int c) : count(c) {}

    Counter& operator++() {
        ++count;
        return *this;
    }

    Counter operator++(int) {
        Counter temp = *this;
        ++(*this);
        return temp;
    }

    int getCount() const {
        return count;
    }
};

int main() {
    Counter c(5);
    std::cout << "Before increment: " << c.getCount() << std::endl;

    ++c; // 前置递增
    std::cout << "After pre-increment: " << c.getCount() << std::endl;

    c++; // 后置递增
    std::cout << "After post-increment: " << c.getCount() << std::endl;

    return 0;
}

        在类外部重载递增运算符:在这里,operator++ 是递增运算符的重载函数。对于前置递增,它返回一个引用,因为递增后的对象可能会在表达式中继续使用。对于后置递增,它返回递增前的对象的副本,因为它需要返回原始值而不是递增后的值。

ClassName& operator++(ClassName& obj) {
    // 实现前置递增操作
    // 修改对象的状态
    return obj; // 返回递增后的对象的引用
}

ClassName operator++(ClassName& obj, int) {
    // 实现后置递增操作
    // 先保存递增前的对象副本
    // 修改对象的状态
    return copy; // 返回递增前的对象的副本
}

        实现在类外部重载前置递增运算符和后置递增运算符的例子:在这个例子中,我们在类外部重载了前置递增运算符和后置递增运算符,并将其声明为友元函数,以便能够访问类的私有成员 count。在重载函数中,我们实现了相应的递增操作,并返回递增后或递增前的对象。

#include <iostream>

class Counter {
private:
    int count;

public:
    Counter(int c) : count(c) {}

    friend Counter& operator++(Counter& obj) {
        ++obj.count;
        return obj;
    }

    friend Counter operator++(Counter& obj, int) {
        Counter temp = obj;
        ++obj;
        return temp;
    }

    int getCount() const {
        return count;
    }
};

int main() {
    Counter c(5);
    std::cout << "Before increment: " << c.getCount() << std::endl;

    ++c; // 前置递增
    std::cout << "After pre-increment: " << c.getCount() << std::endl;

    c++; // 后置递增
    std::cout << "After post-increment: " << c.getCount() << std::endl;

    return 0;
}

        下面给出具体代码分析应用过程,这段代码演示了如何重载递增运算符(++)以及左移运算符(<<),并展示了前置递增、后置递增和输出对象的操作。代码解释:

        首先,定义了一个名为 MyInteger 的类,其中有一个私有成员变量 m_Num,表示自定义的整数类型的值。

        在类内部分别重载了前置递增运算符和后置递增运算符:

   operator++():用于实现前置递增,返回类型为 MyInteger&,即引用类型,以支持链式操作。在该函数内部,将 m_Num 递增,并返回递增后的对象的引用。

   operator++(int):用于实现后置递增,其中 int 是一个占位参数,标识这是后置递增的重载版本。在该函数内部,先记录当前对象的值到临时变量 temp,然后将 m_Num 递增,并返回记录的临时变量。

        在类外部重载了左移运算符 operator<<,用于输出 MyInteger 对象的值。

  test01() 函数演示了前置递增的使用方式,通过连续两次前置递增对对象进行操作,并输出结果。

  test02() 函数演示了后置递增的使用方式,通过后置递增对对象进行操作,并输出结果。

        在 main() 函数中调用了 test02() 函数,测试了后置递增的效果,并输出了相应的结果。

#include <iostream>
using namespace std;

// 重载递增运算符

// 自定义整型
class MyTnteger
{

	friend ostream & operator<<(ostream &cout, MyTnteger myint);
public:
	MyTnteger()
	{
		m_Num = 0;
	}

	// 重载前置++运算符  使用引用返回主要是基于链式编程思想,每一次返回的都是同一个对象,
	// 因此可以持续对通体个对象进行操作
	// 比如 如果使用的是值传递的方式,会正常运行,但是test01()函数里不会输出两个2,而是一个2,一个1
	MyTnteger & operator++()
	{
		// 先进行++运算  使其内部属性进行递增操作
		m_Num++;     //这里怎么写都无所谓,无论++在前还是在后,都是进行加1操作
		// 然后再将自身返回
		return *this;
	}
	// 重载后置++运算符

	MyTnteger  operator++(int)   // int 是一个占位参数,可以实现函数重载
	{
		MyTnteger temp = *this;   // 先记录当前值,再进行++
		m_Num++;   //这里怎么写都无所谓,无论++在前还是在后,都是进行加1操作
		return temp;

	}

private:
	int m_Num;
};


//重载左移运算符
ostream & operator<<(ostream &cout, MyTnteger myint)
{
	cout << myint.m_Num;
	return cout;
}

void test01()
{

	MyTnteger myint;    // 这是自己自定义的一个整型数据
	cout << ++(++myint) << endl;
	cout <<myint << endl;
}

void test02()
{
	MyTnteger myint;
	cout << myint++ << endl;
	cout << myint << endl;

}

int main()
{

	test02();

	system("pause");
	return 0;
}

        示例运行结果如下图所示: 

05-赋值运算符重载       

        赋值运算符(=)重载允许自定义类对象的赋值行为。当重载赋值运算符时,可以定义对象之间的赋值操作的行为,以便在类中使用赋值语句像内置类型一样对对象进行赋值。

        在C++中,可以通过在类内部或类外部定义特定的成员函数或全局函数来重载赋值运算符。以下是分别在类内部和类外部重载赋值运算符的方法。

        在类内部重载赋值运算符:在这里,operator= 是赋值运算符的重载函数。它接受一个常量引用作为参数,即右操作数,然后检查是否进行了自我赋值。如果不是自我赋值,则执行赋值操作。

class ClassName {
public:
    ClassName& operator=(const ClassName& rhs) {
        // 检查自赋值
        if (this != &rhs) {
            // 执行赋值操作
            // 将rhs的值赋给当前对象
        }
        return *this;
    }
};

        实现在类中重载赋值运算符的例子:在这个例子中,MyString 类重载了赋值运算符,以支持对象之间的赋值操作。在赋值运算符的实现中,首先检查是否进行了自我赋值,然后释放原有内存,分配新的内存,并复制字符串内容。

class MyString {
private:
    char* buffer;

public:
    // 构造函数、析构函数和其他成员函数的实现省略

    // 重载赋值运算符
    MyString& operator=(const MyString& rhs) {
        if (this != &rhs) {
            delete[] buffer; // 释放原有内存
            buffer = new char[strlen(rhs.buffer) + 1]; // 分配新内存
            strcpy(buffer, rhs.buffer); // 复制字符串
        }
        return *this;
    }
};

        在类外部重载赋值运算符:在这里,operator= 是赋值运算符的重载函数。它接受两个参数,分别是左操作数(要赋值的对象)和右操作数(赋值的对象的值)。然后检查是否进行了自我赋值,如果不是自我赋值,则执行赋值操作。

ClassName& operator=(ClassName& lhs, const ClassName& rhs) {
    // 检查自赋值
    if (&lhs != &rhs) {
        // 执行赋值操作
        // 将rhs的值赋给lhs对象
    }
    return lhs;
}

       实现在类外部重载赋值运算符的例子:在这个例子中,我们在类外部重载了赋值运算符,以支持对象之间的赋值操作。在重载函数中,首先检查是否进行了自我赋值,然后释放原有内存,分配新的内存,并复制字符串内容。

MyString& operator=(MyString& lhs, const MyString& rhs) {
    if (&lhs != &rhs) {
        delete[] lhs.buffer; // 释放原有内存
        lhs.buffer = new char[strlen(rhs.buffer) + 1]; // 分配新内存
        strcpy(lhs.buffer, rhs.buffer); // 复制字符串
    }
    return lhs;
}

        下面给出具体代码分析应用过程,这段代码演示了如何在类中重载赋值运算符(=),并实现了深拷贝,以避免浅拷贝带来的问题。代码解释如下:

        定义了一个名为 Person 的类,其中包含一个 int 类型的指针成员 m_Age,用于表示人的年龄。

        类中提供了一个有参构造函数,用于初始化 m_Age 成员。

        定义了析构函数,用于释放动态分配的内存。

        在类中重载了赋值运算符 operator=。在赋值运算符的重载函数中,首先进行了深拷贝,即通过动态分配内存来保存对象的年龄值,避免了浅拷贝带来的问题。然后释放原有内存,确保不会发生内存泄漏。

        在 test01() 函数中,创建了三个 Person 对象 p1p2 和 p3,分别表示年龄为 18、20 和 30 岁的人。然后进行了赋值操作 p3 = p2 = p1;,这里演示了连续的赋值操作。

        最后输出了每个人的年龄。

注:

/* c++编译器至少给一个类添加4个函数
    1. 默认构造函数(无参,函数体为空)
    2. 默认析构函数(无参,函数体为空)
    3. 默认拷贝构造函数,对属性进行值拷贝
    4. 赋值运算符 operator=, 对属性进行值拷贝

    如果类中有属性指向堆区,做赋值操作时也会出现深浅拷贝问题

    开辟堆区   使用 new 数据类型
*/

#include <iostream>
using namespace std;


class Person
{
public:

	Person(int age)   //有参构造函数
	{
		m_Age = new int(age);   //  new int()  返回的就是 int *
	}

	~Person()
	{
		if (m_Age != NULL)
		{
			delete m_Age;
			m_Age = NULL;
		}
	}

	// 重载赋值运算符
	Person & operator=(Person &p)
	{
		// 编译器提供是浅拷贝
		// m_Age = p.m_Age

		// 深拷贝解决
		if (m_Age != NULL)
		{
			delete m_Age;
			m_Age = NULL;
		}
		m_Age = new int(*p.m_Age);

		return *this;
	}


	int *m_Age;  //定义为指针形式


};


void test01()
{
	Person p1(18);

	Person p2(20);
    

	/*  如果说还需要做出一个赋值操作,此时,应该再返回的函数那里修改,返回的应该是变量本身
	*/
	Person p3(30);

    p3 =p2 = p1;  //赋值操作


    cout << "p1的年龄 : " << *p1.m_Age << endl;

	cout << "p2的年龄 : " << *p2.m_Age << endl;
	cout << "p3的年龄 : " << *p3.m_Age << endl;

}


int main()
{

	test01();


	system("pause");
	return 0;
}

        示例运行结果如下图所示:

06-关系运算符重载       

        关系运算符(比较运算符)重载允许定义类对象之间的比较操作的行为,使得对象之间可以像内置类型一样进行比较。这些运算符包括等于(==)、不等于(!=)、大于(>)、小于(<)、大于等于(>=)和小于等于(<=)。

        在C++中,可以通过在类内部或类外部定义特定的成员函数或全局函数来重载关系运算符。以下是分别在类内部和类外部重载关系运算符的方法。

        在类内部重载关系运算符:在这里,我们以重载等于运算符(==)为例。operator== 是关系运算符的重载函数。它接受一个常量引用作为参数,即右操作数,然后比较两个对象是否相等,并返回布尔值表示比较结果。

class ClassName {
public:
    bool operator==(const ClassName& rhs) const {
        // 比较对象是否相等,并返回结果
    }

    // 重载其他关系运算符
};

        在类外部重载关系运算符:在这里,我们以重载等于运算符(==)为例。operator== 是关系运算符的重载函数。它接受两个参数,分别是左操作数和右操作数,然后比较两个对象是否相等,并返回布尔值表示比较结果。

bool operator==(const ClassName& lhs, const ClassName& rhs) {
    // 比较两个对象是否相等,并返回结果
}

        下面是一个具体的例子,展示了如何在类中重载等于运算符:在这个例子中,MyNumber 类重载了等于运算符(==),以支持对象之间的相等性比较。在重载函数中,它比较了两个对象的 value 成员是否相等,并返回比较结果。

class MyNumber {
private:
    int value;

public:
    MyNumber(int val) : value(val) {}

    bool operator==(const MyNumber& rhs) const {
        return this->value == rhs.value;
    }
};

        下面给出具体代码分析应用过程,这段代码演示了如何在类中重载关系运算符(==),并实现了自定义的对象相等性比较。代码解释如下:

        定义了一个名为 Person 的类,其中包含两个成员变量 m_Name 和 m_Age,分别表示人的姓名和年龄。

        类中提供了一个构造函数,用于初始化 m_Name 和 m_Age 成员。

        在类中重载了等于运算符 operator==。在重载函数中,通过比较两个 Person 对象的 m_Name 和 m_Age 成员是否相等,来判断两个对象是否相等,并返回相应的布尔值。

        在 test01() 函数中,创建了两个 Person 对象 p1 和 p2,它们的姓名和年龄都相同。然后通过 if (p1 == p2) 来判断它们是否相等,并输出相应的结果。

        最后输出了判断结果。

#include <iostream>
using namespace std;

// 重载关系运算符

class Person
{
public:
	Person(string name, int age)
	{
		m_Name = name;
		m_Age = age;
	}

	// 重载
	bool operator==(Person &p)
	{
		if (this->m_Name==p.m_Name && this->m_Age==p.m_Age)
		{
			return true;
		}
		return false;
	}
	string m_Name;
	int m_Age;

};

void test01()
{
	Person p1("张三", 18);
	Person p2("张三", 18);

	if (p1 == p2)
	{
		cout << "p1和p2是相等的" << endl;
	}
}

int main()
{
	test01();

	system("pause");
	return 0;
}

        示例运行结果如下图所示:

07-函数调用运算符重载       

       函数调用运算符(也称为仿函数或函数对象)允许你将对象像函数一样调用,这样你可以像调用函数一样使用对象。在C++中,你可以通过在类中重载函数调用运算符 operator() 来实现这一点。

        下面是重载函数调用运算符的一般形式:在这里,Functor 是函数对象的类名,ReturnType 是调用运算符返回的类型,ParameterType1ParameterType2 等是调用运算符接受的参数类型。

class Functor {
public:
    ReturnType operator()(ParameterType1 arg1, ParameterType2 arg2, ...) {
        // 函数体
    }
};

        具体的例子来解释:在这个例子中,MyFunctor 类重载了函数调用运算符 operator()。在 operator() 函数中,它输出传入的参数。在 main 函数中,我们创建了一个 MyFunctor 类的对象 myFunc,然后像调用函数一样使用该对象,并传入参数 10

#include <iostream>
using namespace std;

class MyFunctor {
public:
    void operator()(int x) {
        cout << "Function called with argument: " << x << endl;
    }
};

int main() {
    MyFunctor myFunc;

    // 将对象 myFunc 像调用函数一样调用
    myFunc(10);

    return 0;
}

        下面给出具体代码分析应用过程,这段代码演示了如何使用函数调用运算符重载,也称为仿函数(functor)。代码解释如下:

        首先定义了一个名为 MyPrint 的类,它实现了函数调用运算符 operator()。在 operator() 函数中,它接受一个 string 类型的参数 text,然后将该参数输出到标准输出流中。

        在 test01() 函数中,创建了一个 MyPrint 类的对象 myFunc,然后像调用函数一样使用该对象,并传入字符串参数 "hello world"。这样就会调用 MyPrint 类中重载的函数调用运算符,输出 "hello world" 到标准输出流中。

        接着定义了一个名为 MyAdd 的类,它也实现了函数调用运算符 operator()。在 operator() 函数中,它接受两个 int 类型的参数 v1 和 v2,然后返回它们的和。

        在 test02() 函数中,创建了一个 MyAdd 类的对象 add,然后像调用函数一样使用该对象,并传入整数参数 10 和 10。这样就会调用 MyAdd 类中重载的函数调用运算符,计算并返回 10 + 10 的结果,最后输出到标准输出流中。

        还展示了匿名对象调用的方式,通过 MyAdd()(100, 100) 这样的形式,直接创建一个 MyAdd 类的匿名对象,并调用其函数调用运算符,并传入整数参数 100 和 100,以此来计算并输出结果。

#include <iostream>
using namespace std;
#include <string>

class MyPrint
{
public:
	void operator()(string text)
	{
		cout << text << endl;
	}
};

void test01()
{
	//重载的()操作符 也称为仿函数
	MyPrint myFunc;
	myFunc("hello world");
}

class MyAdd
{
public:
	int operator()(int v1, int v2)
	{
		return v1 + v2;
	}
	
};

void test02()
{
	MyAdd add;
	int ret = add(10, 10);
	cout << "ret = " << ret << endl;
	//匿名对象调用
	cout << "MyAdd()(100,100) = " << MyAdd()(100, 100) << endl;
}

int main() {

	test01();
	test02();
	system("pause");
	return 0;
}

        示例运行结果如下图所示:

总结       

        运算符重载是C++中一个强大的特性,它允许你重新定义运算符的行为,使得它们能够用于自定义类型的对象上。下面是一些运算符重载的总结:

        语法:运算符重载是通过定义一个成员函数或全局函数来完成的,其函数名是 operator 后接要重载的运算符符号。例如,operator+ 用于重载加法运算符。

        成员函数 vs 全局函数:你可以选择将运算符重载定义为类的成员函数或全局函数。成员函数的左操作数是调用对象,而全局函数的左操作数是第一个参数。

        返回类型:对于大多数运算符,重载函数应该返回一个值,通常是引用类型以支持连续操作。

        参数:运算符重载函数的参数取决于运算符本身的操作数。一元运算符可能只有一个参数,而二元运算符可能有两个参数。

        一元运算符 vs 二元运算符:一元运算符只作用于一个操作数,如递增运算符(++);而二元运算符作用于两个操作数,如加法运算符(+)。

        重载示例:常见的运算符重载包括加法、减法、乘法、除法、赋值、递增、递减、等于、不等于等。重载它们可以使得自定义类型的对象能够像内置类型一样使用这些运算符。

        注意事项:在重载运算符时,需要遵循运算符的基本语义,以确保代码的清晰性和可预测性。同时,避免过度使用运算符重载,以免导致代码难以理解。

        友元函数:有时候需要在类外部重载运算符并访问类的私有成员,可以将重载函数声明为类的友元函数。

        总的来说,运算符重载是C++中的一个强大工具,能够提高代码的可读性和灵活性,但也需要谨慎使用,遵循良好的编程实践。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/632445.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

AVDemo漏洞平台黑盒测试

信息收集 说明一下&#xff1a; 因为是本地的环境&#xff0c;端口这些就不扫描了&#xff0c; 还有这个是某个dalao写的平台&#xff0c;也就检测不到什么cms了&#xff0c; 信息收集&#xff0c;端口&#xff0c;cms这些是必做的&#xff0c; 首先&#xff0c;这里先简单的…

致命错误: 用户 “system“ Password 认证失败 (kbjdbc: autodetected server-_enco

问题在于用户权限不足&#xff0c;修改kingbase安装目录data目录下的的文件sys_hba.conf&#xff0c;修改IPV4部分本地验证方式为trust即可 修改后&#xff0c;打开资源管理器&#xff0c;通过服务找到人大金仓服务&#xff0c;重新启动即可

有趣的css - 文字隐身术效果

大家好&#xff0c;我是 Just&#xff0c;这里是「设计师工作日常」&#xff0c;今天分享的是利用动画属性来模拟文字隐身消失的效果。 《有趣的css》系列最新实例通过公众号「设计师工作日常」发布。 目录 整体效果核心代码html 代码css 部分代码 完整代码如下html 页面css 样…

ip显示地址和实际地址不一样:原因解析与应对策略

在数字化时代&#xff0c;IP地址作为我们在互联网上的身份标识&#xff0c;其重要性不言而喻。然而&#xff0c;有时我们会遇到ip显示地址和实际地址不一样的情况&#xff0c;这不仅可能影响到我们的网络体验&#xff0c;还可能引发一系列安全和隐私问题。那么&#xff0c;造成…

嵌入式学习72-复习(字符设备驱动框架)

编辑 drivers/char/Kconfig 为了在make menuconfig是能够显示出我们写的驱动程序 make menuconfig 编辑 drivers/char/Makefile 才是真正把编写好的源文件加入到编译中去 make modules cp drivers/char/first_driver.ko ~/nfs/rootfs/

26版SPSS操作教程(高级教程第二十三章)

目录 前言 粉丝及官方意见说明 第二十三章一些学习笔记 第二十三章一些操作方法 时间序列模型 时间序列的建立和平稳化 数据假设 具体操作 定义时间变量 时间序列的平稳化 绘制相应的时间序列图 序列图 自相关图&#xff08;autocorrelation chart&#xff09; 对…

03-行为型模式(共10种)

上一篇: 02-结构型设计模式(共7种) 1. Strategy(策略模式) 策略模式是一种行为型设计模式&#xff0c;它定义了一系列算法&#xff0c;将每个算法封装到独立的类中&#xff0c;并使它们可以互相替换。这样可以使算法的变化独立于使用算法的客户端。 在 C 中&#xff0c;策略模式…

LeetCode-1953. 你可以工作的最大周数【贪心 数组】

LeetCode-1953. 你可以工作的最大周数【贪心 数组】 题目描述&#xff1a;解题思路一&#xff1a;贪心&#xff0c;关键点是找到(工作最长时间的工作longest )与(剩余其他工作加起来的时间总和rest )的关系。解题思路二&#xff1a;证明解题思路三&#xff1a;简化 题目描述&am…

【c++算法篇】滑动窗口

&#x1f525;个人主页&#xff1a;Quitecoder &#x1f525;专栏&#xff1a;算法笔记仓 目录 1.长度最小的子数组2.无重复字符的最长子串3.最大连续1的个数 III4.将 x 减到 0 的最小操作数5.水果成篮6.找到字符串中所有字母异位词7.串联所有单词的子串8.最小覆盖子串 滑动窗…

系统架构师考试(二)

敏捷方法 CMMI代表Capability Maturity Model Integration&#xff0c;是一种用于评估和改进组织软件工程和系统工程的模型。CMMI提供一个框架&#xff0c;帮助组织评估其软件和系统工程的成熟度&#xff0c;该模型基于过程成熟度模型&#xff08;CMM&#xff09;和集成项目管理…

Java 如何去规避一些没用的类型转换?

在Java编程中&#xff0c;类型转换&#xff08;Type Casting&#xff09;是将一个数据类型的值转换为另一个数据类型的过程。这在某些情况下是必要的&#xff0c;但滥用类型转换会导致代码变得复杂、难以维护&#xff0c;并且可能引发运行时错误。规避不必要的类型转换不仅能提…

Online RL + IL : Active Policy Improvement from Multiple Black-box Oracles

ICML 2023 paper code 紧接上一篇MAMBA&#xff0c;本文在同种问题设定下的在线模仿学习方法。 Intro 文章提出了一种新的模仿学习算法&#xff0c;名为 MAPS&#xff08;Max-aggregation Active Policy Selection&#xff09;和其变体 MAPS-SE&#xff08;Max-aggregation A…

Kibana使用

一、什么是Kibana   Kibana 是一个开源的分析和可视化平台&#xff0c;Kibana 提供搜索、查看和与存储在 Elasticsearch 索引中的数据进行交互的功能。开发者或运维人员可以轻松地执行高级数据分析&#xff0c;并在各种图表、表格和地图中可视化数据。 Kibana使用&#xff1a…

【御控物联】物联网协议

文章目录 一、前言二、协议清单三、技术资料 一、前言 如果一个人想要和全球各个国家贸易&#xff0c;那这个人就得懂各个国家的语言或者全球通用语言&#xff0c;同样&#xff0c;在物联网的世界里&#xff0c;各家设备也都拥有自己的语言&#xff08;协议&#xff09;&#…

JUnit5参数化用例(三)

JUnit5枚举参数的参数化&#xff1a; 使用枚举类作为测试数据枚举参数参数化注解EnumSource必须与ParameterizedTest结合使用 枚举参数化注解 -简单使用&#xff1a; 需要添加EnumSource注解测试方法传入枚举类作为参数 在执行前&#xff0c;我们需了解enum枚举的使用方式&…

弘君资本午评:沪指拉升涨0.48%,地产板块爆发,金融等板块上扬

16日早盘&#xff0c;两市股指盘中震动上扬&#xff0c;创业板指涨超1%&#xff1b;场内近3800股飘红&#xff0c;半日成交超5000亿元。 到午间收盘&#xff0c;沪指涨0.48%报3134.97点&#xff0c;深证成指涨0.83%&#xff0c;创业板指涨1.14%&#xff0c;两市算计成交5194亿…

在win10折腾Flowise:部署和尝试

Flowise 是一种低代码/无代码拖放工具&#xff0c;旨在让人们轻松可视化和构建 LLM 应用程序。 本地部署 操作系统&#xff1a; win10 由于网络、操作系统等各种未知问题&#xff0c;使用npm install -g flowise的方式&#xff0c;尝试了很多次&#xff0c;都没有部署成功&am…

AI大模型试用盘点(附体验网址)包含10多款大模型

【前言】目前我是用过的公开免费使用的文心一言、智谱清言比较好! 1、阿里云——通义千问 网址:https://tongyi.aliyun.com/ 简介:阿里达摩院推出的大模型,拥有千亿参数,可用于智能问答、知识检索、文案创作等场景。 演示: 功能大全: 2、科大讯飞——星火大模型…

数据库-索引结构(B-Tree,B+Tree,Hash,二叉树)

文章目录 索引结构有哪些&#xff1f;二叉树详解&#xff1f;B-Tree详解?BTree详解&#xff1f;Hash详解&#xff1f;本篇小结 更多相关内容可查看 索引结构有哪些&#xff1f; MySQL的索引是在存储引擎层实现的&#xff0c;不同的存储引擎有不同的索引结构&#xff0c;主要包…

【C语言】static关键字的妙用

前言 在c/c中存在着一种内存结构&#xff0c;以此为栈区、堆区、静态区&#xff08;这里是大致划分&#xff0c;不细究&#xff09; 存放规则如下&#xff1a; 栈区&#xff1a;存放局部变量、函数的形参、临时属性的变量 堆区&#xff1a;存放malloc、realloc、calloc、fr…