【C++】运算符重载

2、 运算符重载(掌握)

2.1 概念

运算符重载,就是对已有的运算符重新进行定义,赋予其另一种功能,以适应不同的数据类型

运算符重载的目的:简化操作 让已有的运算符 适应适应不同的数据类型。

C++中可以把部分运算符看做成函数,此时运算符也可以重载。

运算符预定义的操作只能针对基本数据类型,但是对于自定义类型,也需要类似的运算操作,此时就可以重新定义这些运算符的功能,使其支持特定类型,完成特定的操作。

语法:函数的名字由关键字operator及其紧跟的运算符组成

比如:重载+运算符 ==>operator+ 重载=号运算 ==>operator=

注意:重载运算符 不要更改 运算符的本质操作(+是数据的相加 不要重载成相减)

运算符重载有两种实现方式:

● 友元函数运算符重载

● 成员函数运算符重载

2.2 函数运算符重载

2.2.1运算符<<的重载
#include <iostream>
#include<string.h>
using namespace std;
class Person
{
private:
	char *name;
	int num;

public:
    Person(char *name, int num)
    {
        this‐>name = new char[strlen(name)+1];
    	strcpy(this‐>name,name);
        this‐>num = num;
        cout<<"有参构造"<<endl;
    }

      //普通的成员函数
    void printPerson(void)
    {
    	cout<<"name = "<<name<<", num = "<<num<<endl;
    }
     ~Person()
    {
    	if(this‐>name != NULL)
    	{
            delete [] this‐>name;
            this‐>name = NULL;
     	}
    	cout<<"析构函数"<<endl;
    }
};
int main(int argc, char *argv[])
{
    Person ob1("lucy",18);
    //普通的成员函数 遍历信息
    ob1.printPerson();
    
    //cout默认输出方式 无法识别 自定义对象 输出格式
    //cout<<ob1<<endl;//err
    
    return 0;
 }

解决此问题

解决办法:将operator<<设置成友元:

#include <iostream>
#include<string.h>
using namespace std;
class Person
{
	//设置成友元函数 在函数内 访问Person类中的所有数据
	friend ostream& operator<<(ostream &out, Person &ob);
private:
    char *name;
    int num;
public:
	Person(char *name, int num)
	{
    	this‐>name = new char[strlen(name)+1];
    	strcpy(this‐>name,name);
    	this‐>num = num;
    	cout<<"有参构造"<<endl;
	}
	//普通的成员函数
    void printPerson(void)
    {
    	cout<<"name = "<<name<<", num = "<<num<<endl;
    }
    ~Person()
    {
        if(this‐>name != NULL)
        {
            delete [] this‐>name;
            this‐>name = NULL;
        }
    	cout<<"析构函数"<<endl;
    }
};

ostream& operator<<(ostream &out, Person &ob)//out=cout, ob =ob1
{
	//重新实现 输出格式
	out<<ob.name<<", "<<ob.num;

	//每次执行为 返回值得到cout
	return out;
}
int main(int argc, char *argv[])
{
    Person ob1("lucy",18);
    //普通的成员函数 遍历信息
    //ob1.printPerson();
    
    //cout默认输出方式 无法识别 自定义对象 输出格式
    //cout<<ob1<<endl;//err
    
     //运算符重载的调用方式1:
    operator<<(cout, ob1)<<endl;
    
    //运算符重载的调用方式2:
    //对方法1 进行优化 去掉operator,第一个参数 放在运算符<<的左边 第二个参数 放在运算符<<的右边
    cout<<ob1<<endl;//等价operator<<(cout, ob1);
    
    Person ob2("bob",19);
    cout<<ob1<<" "<<ob2<<endl;//迭代操作用&
    
    return 0;
}

2.2.2重载+运算符:

全局函数作为友元 完成运算符重载+

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

class Person
{
    friend ostream& operator<<(ostream& out, Person& ob); // 友元声明,全局函数作为友元,完成运算符重载 <<
    friend Person operator+(Person& ob1, Person& ob2);     // 友元声明,全局函数作为友元,完成运算符重载 +

private:
    char* name; // 人物名字
    int num;    // 人物编号

public:
    // 无参构造函数
    Person()
    {
        this->name = NULL;
        this->num = 0;
        cout << "无参构造" << endl;
    }

    // 有参构造函数
    Person(char* name, int num)
    {
        // 使用 new 关键字动态分配内存,存储人物名字,并进行初始化
        this->name = new char[strlen(name) + 1];
        strcpy(this->name, name);
        this->num = num;
        cout << "有参构造" << endl;
    }

    // 普通成员函数,用于打印对象的数据
    void printPerson(void)
    {
        cout << "name = " << name << ", num = " << num << endl;
    }

    // 析构函数,用于释放动态分配的内存
    ~Person()
    {
        if (this->name != NULL)
        {
            delete[] this->name;
            this->name = NULL;
        }
        cout << "析构函数" << endl;
    }
};

// 友元函数,完成运算符重载 <<
ostream& operator<<(ostream& out, Person& ob)
{
    // 重新实现输出格式
    out << ob.name << ", " << ob.num;

    // 每次执行为返回值得到 cout
    return out;
}

// 友元函数,完成运算符重载 +
Person operator+(Person& ob1, Person& ob2)
{
    // name + name(字符串追加)
    char* tmp_name = new char[strlen(ob1.name) + strlen(ob2.name) + 1];
    strcpy(tmp_name, ob1.name);
    strcat(tmp_name, ob2.name);

    // num + num(数值相加)
    int tmp_num = ob1.num + ob2.num;

    // 创建临时对象并返回
    Person tmp(tmp_name, tmp_num);

    // 释放 tmp_name 的空间
    if (tmp_name != NULL)
    {
        delete[] tmp_name;
        tmp_name = NULL;
    }

    return tmp;
}

void test02()
{
    Person ob1("lucy", 18);
    Person ob2("bob", 19);

    cout << ob1 << endl;
    cout << ob2 << endl;

    // 调用运算符重载 +,实现 ob1 + ob2
    Person ob3 = ob1 + ob2;

    // 调用运算符重载 <<,实现 cout << ob3
    cout << ob3 << endl;
}

int main(int argc, char* argv[])
{
    test02();

    return 0;
}

1.成员函数 完成运算符重载+

#include <iostream>
#include <cstring> // 包含头文件,用于使用字符串操作函数
using namespace std;

class Person
{
    // 友元声明,全局函数作为友元,完成运算符重载 <<
    friend ostream &operator<<(ostream &out, Person &ob);

private:
    char *name; // 人物名字
    int num;    // 人物编号

public:
    // 无参构造函数
    Person()
    {
        this->name = NULL;
        this->num = 0;
        cout << "无参构造" << endl;
    }

    // 有参构造函数
    Person(char *name, int num)
    {
        // 使用 new 关键字动态分配内存,存储人物名字,并进行初始化
        this->name = new char[strlen(name) + 1];
        strcpy(this->name, name);
        this->num = num;
        cout << "有参构造" << endl;
    }

    // 运算符重载 +,ob1 用 this 代替,ob2 用参数 ob 代替
    Person operator+(Person &ob)
    {
        // this ==> &ob1

        // name + name(字符串追加)
        char *tmp_name = new char[strlen(this->name) + strlen(ob.name) + 1];
        strcpy(tmp_name, this->name);
        strcat(tmp_name, ob.name);

        // num + num(数值相加)
        int tmp_num = this->num + ob.num;

        // 创建临时对象并返回
        Person tmp(tmp_name, tmp_num);

        // 释放 tmp_name 的空间
        if (tmp_name != NULL)
        {
            delete[] tmp_name;
            tmp_name = NULL;
        }

        return tmp;
    }

    // 普通成员函数,用于打印对象的数据
    void printPerson(void)
    {
        cout << "name = " << name << ", num = " << num << endl;
    }

    // 析构函数,用于释放动态分配的内存
    ~Person()
    {
        if (this->name != NULL)
        {
            delete[] this->name;
            this->name = NULL;
        }
        cout << "析构函数" << endl;
    }
};

// 友元函数,完成运算符重载 <<
ostream &operator<<(ostream &out, Person &ob)
{
    // 重新实现输出格式
    out << ob.name << ", " << ob.num;

    // 每次执行为返回值得到 cout
    return out;
}

// 测试函数
void test03()
{
    // 创建两个 Person 对象
    Person ob1("lucy", 18);
    Person ob2("bob", 19);

    // 调用运算符重载 +,实现 ob1 + ob2
    Person ob3 = ob1 + ob2;
	Person ob3 = ob1.operator+(ob2)
    
    // 调用运算符重载 <<,实现 cout << ob3
    cout << ob3 << endl;
}

// 主函数
int main(int argc, char *argv[])
{
    // 调用测试函数
    test03();

    return 0;
}

2.2.3 重载自增 或自减 ++ --运算符

运算符表达式的函数对应关系如下:

友元函数重载

#include <iostream>
using namespace std;


class MyInt
{
private:
int a;
public:
MyInt(int a):a(a){}

int get_int()
{
    return a;
}

// 友元+运算符重载
friend MyInt operator +(MyInt &i1,MyInt &i2);
};

// 友元+运算符重载,类外实现
MyInt operator +(MyInt &i1,MyInt &i2)
{
    MyInt int4(0);
    int4.a = i1.a + i2.a;
    return int4;
}


int main()
{
    MyInt int1(2);
    MyInt int2(int1);   // 拷贝构造函数

    MyInt int3 = int1 + int2;

    cout << int3.get_int() << endl;
    return 0;
}

友元函数运算符重载,实现前置++与后置++

#include <iostream>
using namespace std;


class MyInt
{
private:
int a;
public:
MyInt(int a):a(a){}

int get_int()
{
    return a;
}

// 友元+运算符重载
friend MyInt operator +(MyInt &i1,MyInt &i2);
friend MyInt operator ++(MyInt &i); // 前置++
friend MyInt operator ++(MyInt &i,int); // 后置++
};

// 友元+运算符重载,类外实现
MyInt operator +(MyInt &i1,MyInt &i2)
{
    MyInt int4(0);
    int4.a = i1.a + i2.a;
    return int4;
}

MyInt operator ++(MyInt &i)
{
    // int - > MyInt 触发构造函数隐式调用
    return ++i.a;
}

MyInt operator ++(MyInt &i,int)
{
    // int - > MyInt 触发构造函数隐式调用
    return i.a++;
}


int main()
{
    MyInt int1(1);
    MyInt int2(int1);   // 拷贝构造函数

    MyInt int3 = int1 + int2;

    cout << (++int1).get_int() << endl; // 前置++输出2
    cout << (int1++).get_int() << endl; // 后置++输出2
    cout << int1.get_int() << endl;    // 输出3
    cout << int3.get_int() << endl;
    return 0;
}

2.

2.3.3.1++成员函数运算符重载

成员函数运算符重载相比较于友元函数运算符重载,最主要的区别在于,友元函数的第一个输入参数,在成员函数运算符重载中使用this指针代替。因此通向的运算符重载,成员函数比友元函数参数少一个。

#include <iostream>
using namespace std;


class MyInt
{
private:
	int a;
public:
	MyInt(int a):a(a){}

int get_int()
{
    return a;
}

    // 成员函数运算符重载,声明
    MyInt operator +(MyInt &i2);
    MyInt operator ++(); // 前置++
    MyInt operator ++(int); // 后置++
};

// 友元+运算符重载,类外实现
MyInt MyInt::operator +(MyInt &i2)
{
    return this->a + i2.a;
}

MyInt MyInt::operator ++()
{
    // int - > MyInt 触发构造函数隐式调用
    return ++this->a;
}

MyInt MyInt::operator ++(int)
{
    // int - > MyInt 触发构造函数隐式调用
    return this->a++;
}


int main()
{
    MyInt int1(1);
    MyInt int2(int1);   // 拷贝构造函数

    MyInt int3 = int1 + int2;

    cout << (++int1).get_int() << endl; // 前置++输出2
    cout << (int1++).get_int() << endl; // 后置++输出2
    cout << int1.get_int() << endl;    // 输出3
    cout << int3.get_int() << endl;
    return 0;
}

operator++ 编译器看到++a(前置++),它就调用operator++(a),

当编译器看到a++(后置++),它就 会去调用operator++(a,int)

#include <iostream>
#include <string.h>

using namespace std;

class Data
{
   friend ostream& operator <<(ostream &out,Data &ob);

private :
    int a;
    int b;
public:
    Data()
    {
        cout<<"无参构造函数"<<endl;
        a=0;
        b=0;
    }
    Data(int a,int b):a(a),b(b)
    {
        cout<<"有参构造函数"<<endl;
        //this->a=a;
        //this->b=b
    }
    void showData(void)
    {
        cout<<"a= "<< a <<"  "<<"b= "<< b <<endl;
    }
    ~Data()
    {
        cout <<"析构构造函数"<<endl;
    }


    //成员函数 重载前置++ ++ob1 (先加 后使用)
    //编译器 默认识别 operator++(a) //但是a可以用this代替 从而化简 operator++()

    Data& operator ++()
    {
        a++; //this->a = this->a+1
        b++;//this->b = this->b+1
        return *this;
    }

    //成员函数 重载后置++ ob1++ (先使用 后加)
   //编译器 默认识别 operator++(a,int) //但是a可以用this代替 从而化简 operator++(int)
	//static 是全局变量  不加&会重新开辟空间
    Data& operator ++(int)
    {
        //先使用(备份加之前的值)
        static Data old = *this;//取内容
        //后加
        a++;
        b++;

        //返回备份值
        return old;
    }
    //重载前置‐‐ ‐‐ob3
    //编译器 默认识别 operator++(a) //但是a可以用this代替 从而化简 operator‐‐()
    Data& operator --()
    {
       //先减
        a--;
        b--;
        return *this;
    }

    //重载后‐‐ ob4‐‐
    //编译器 默认识别 operator++(a,int) //但是a可以用this代替 从而化简 operator++(int)
    Data& operator--(int)
    {
        //先使用 备份
        static Data old = *this;
        a--;
        b--;
        return old;
    }
};
//普通全局函数 作为类的友元 重载<<运算符
ostream& operator <<(ostream &out,Data &ob)
{
    out<<"a= "<<ob.a<<",b= "<<ob.b;
    return out;
}

void test01()
{
    Data ob1(10,20);
    ob1.showData();

    //重载<<直接输出自定义对象的值
    cout<<ob1<<endl;//operator<<(cout,ob1);
    //成员函数 重载 ++运算符
    cout<<++ob1<<endl;
    Data ob2(10,20);
    cout<<ob2++<<endl;
    cout<<ob2<<endl;

   //成员函数 重载 ‐‐运算符
    Data ob3(10,20);
    cout<<"ob3 "<<ob3<<endl;
    cout<<--ob3<<endl;

    Data ob4(10,20);
    cout<<"ob4 "<<ob4<<endl;
    cout<<ob4--<<endl;
    cout<<"ob4 "<<ob4<<endl;
}

int main()
{

    test01();

    return  0;
}

2.4 其他运算符重载

部分运算符重载不支持友元函数运算符重载,只支持成员函数运算符重载,比如说(类型转换运算符重载,必须使用成员函数运算符重载)。

2.4.1 赋值运算符重载 =运算符 (重要)

如果程序员不手动编写赋值运算符重载,则赋值运算符重载会被编译器自动添加。

#include <iostream>
using namespace std;


class MyInt
{
private:
	int a;
public:
	MyInt(int a):a(a){}

    int get_int()
    {
        return a;
    }

    // 编译器自动添加的赋值运算符重载
    MyInt &operator =(MyInt &i)
    {
        cout << "赋值运算符重载被调用了" << endl;
        this->a = i.a;
        return *this;   // 返回当前对象
    }

};


int main()
{
    MyInt int1(1);
    MyInt int2(int1);   // 拷贝构造函数

    MyInt int3(2);
    int3 = int1;    // 赋值运算符重载
    MyInt int4 = int3;  // 隐式拷贝构造函数
    // int4 = int2;

    cout << int3.get_int() << endl;
    return 0;
}

当类中出现指针类型的成员变量,默认的赋值运算符重载会出现类似于默认的拷贝构造函数,中的“浅拷贝”的问题。因此也需要手动编写解决“浅拷贝”的问题。

2.4.2类型转换运算符重载

必须使用成员函数运算符重载,且格式比较特殊。

#include <iostream>
using namespace std;


class MyInt
{
private:
    int a;
    string str = "hello";
public:
MyInt(int a):a(a){}

    int get_int()
    {
        return a;
    }

    // 类型转换运算符重载
    operator int()
    {
        return a;
    }
    
    operator string()
    {
        return str;
    }


};


int main()
{
    MyInt int1(1);

    string a = int1;

    cout << a << endl;
    return 0;
}
2.4.3 【指针运算符 * ->】(了解)

设计一个智能指针 解决 Person new出的堆区空间 释放问题

#include <iostream>
#include <string.h>

using namespace std;

class Person
{


private :
    int num;

public:
    Person(int num):num(num)
    {
        //this‐>num = num;
        cout <<"有参函数构造 num="<<num<<endl;
    }
    void showPerson(void)
    {
        cout <<"num="<<num<<endl;
    }
    ~Person()
    {
        cout<<"析构函数 num = "<<num<<endl;
    }

};
//设计一个智能指针 解决 Person new出的堆区空间 释放问题
class SmartPointer
{
    public:
    Person *pPerson;

    public:
    SmartPointer(Person *p)
    {
        pPerson = p;
    }

    ~SmartPointer()
    {
        if(pPerson != NULL)
        {
            delete pPerson;
            pPerson = NULL;
        }
    }

    //成员函数重载‐>运算符
    Person* operator->()
    {
        return this->pPerson;
    }

     //成员函数 重载 *运算
    Person& operator*()
    {
        return *(this->pPerson);
    }


};

void test01()
{

    Person *p =new Person(100);
    p->showPerson();

    //假如我忘了 delete p
    delete p;

    //需求:自动的帮我释放 堆区空间(智能指针的概念)
    SmartPointer pointer(new Person(200));

    //访问Person类中的showPerson()
    //pointer.pPerson‐>showPerson();
    //保证指针的使用
    //(pointer.operator ‐>())‐>showPerson();
    pointer->showPerson();
    (*pointer).showPerson();

}

int main(int argc ,char const*argv[])
{

    test01();

    return 0;
}

#include <iostream>
#include <string>

using namespace std;

class String {
public:
    // 构造函数
    String(const string& s) : str(s) {}

    // 友元运算符重载
    // 实现字符串连接
    friend String operator +(const String& s1, const String& s2);

    // 实现字符串长度比较
    friend bool operator >(const String& s1, const String& s2);
    friend bool operator <(const String& s1, const String& s2);
    friend bool operator ==(const String& s1, const String& s2);

    // 实现字符串不等于比较
    bool operator !=(const String& other);

    // 赋值运算符重载
    String& operator =(const String& s);

    // 类型转换运算符,将 String 转换为 string
    operator string();

    // 成员函数,获取字符串
    string get_str();

private:
    string str;
};

// 友元运算符重载,实现字符串连接
String operator +(const String& s1, const String& s2)
{
    String result = s1.str + s2.str;
    return result;
}

// 友元运算符重载,实现字符串长度比较
bool operator >(const String& s1, const String& s2)
{
    return s1.str.size() > s2.str.size();
}

bool operator <(const String& s1, const String& s2)
{
    return s1.str.size() < s2.str.size();
}

bool operator ==(const String& s1, const String& s2)
{
    return s1.str.size() == s2.str.size();
}

// 成员函数运算符重载,实现字符串不等于比较
bool String::operator !=(const String& other)
{
    return str.size() != other.str.size();
}

// 赋值运算符重载
String& String::operator =(const String& s)
{
    if (this != &s) {
        str = s.str;
    }
    return *this;
}

// 类型转换运算符,将 String 转换为 string
String::operator string()
{
    return str;
}

// 成员函数,获取字符串
string String::get_str()
{
    return str;
}

int main() {
    String s1("Hello");
    String s2("World");

    // 使用友元运算符重载实现字符串连接
    String result = s1 + s2;
    cout << "连接结果: " << result.get_str() << endl;

    // 使用友元运算符重载实现字符串比较
    cout << "比较结果: " << (s1 > s2 ? "s1更大" : "s2 更大") << endl;

    // 使用成员函数运算符重载实现字符串不等于比较
    cout << "比较结果: " << (s1 != s2 ? "不等于" : "等于") << endl;

    // 使用类型转换运算符将 String 转换为 string
    string str_result = static_cast<string>(result);
    cout << "转换结果: " << str_result << endl;

    return 0;
}

2.5 注意事项

● 重载的运算符限制在C++语言中已有的运算符范围,不能创建新的运算符。

● 运算符重载本质上也是函数重载,但是不支持函数默认值设定。

● 重载之后的运算符不能改变运算符的优先级和结合性,也不能改变运算符的操作数和语法结构。

● 运算符重载必须基于或包含自定义类型,即不能改变基本数据类型的运算规则。

● 重载的功能应该与原有功能类似。避免滥用运算符重载。

● 一般情况下,双目运算符,建议使用友元函数运算符重载,单目运算符建议使用成员函数运算符重载。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值