类和对象(10)--运算符重载

本文详细介绍了C++中运算符重载的概念,包括加号、左移、递增、指针、赋值、[]、关系和函数调用等运算符的重载方法,以及注意事项和常见应用场景。特别强调了成员函数与全局函数的选择,以及智能指针和赋值运算符的深拷贝技巧。

1 运算符重载

所谓重载,就是重新赋予新的含义。函数重载就是对一个已有的函数赋予新的含义,使之实现新功能,因此,一个函数名就可以用来代表不同功能的函数,也就是”一名多用”。

运算符重载的本质是函数重载。

重载函数的一般格式如下: 

函数类型 operator 运算符名称(形参)
{
   重载实体;
}


1.1 加号运算符重载

成员函数:T operator+(T &t1)
全局函数:T operator+(T &t1, T &t2)

  1. 可以利用成员函数实现加号运算符重载
    本质:(*this).operator+(m1) (结合代码看)
    注意:可以实现 m1 + m2 + m3
  2. 可以利用全局函数加友元实现加号运算符重载
    本质:operator+(m1, m2) (结合代码看)
    注意:不能实现 m1 + m2 + m3
  3. 通过以上两点,所以使用成员函数实现加号运算符重载

#include <iostream>
using namespace std;

class Myint
{
    //friend Myint operator+(Myint &m1, Myint &m2);
public:
    Myint(){}

    Myint(int a)
    {
        this->m_A = a;
    }

#if 1
    // 1.利用成员函数实现加号运算符重载
    // 本质: (*this).operator+(m1)
    // 可以实现 m1 + m2 + m3
    Myint operator+(Myint &m1)
    {
        Myint temp;
        temp.m_A = this->m_A + m1.m_A;
        return temp;
    }
#endif

    int m_A;
};

#if 0
// 2.利用全局函数加友元实现加号运算符重载
// 本质: operator+(m1, m2)
// 不能实现 m1 + m2 + m3
Myint operator+(Myint &m1, Myint &m2)
{
    Myint temp;
    temp.m_A = m1.m_A + m2.m_A;
    return temp;
}
#endif


int main()
{
    Myint a(10);
    Myint b(20);
    Myint c(30);

    Myint d;
    Myint e;

    // 如果利用全局函数加友元实现加号运算符重载,不能实现
    d = a + b + c;

    // 全局函数和成员函数都可以实现
    e = a + d;

    cout << d.m_A << endl;
    cout << e.m_A << endl;
}


1.2 左移运算符重载

全局函数:ostream& operator<<(ostream &cout, T &t1)
成员函数:ostream& operator<<(ostream &cout)

  1. 如果利用成员函数重载 ,无法实现让cout 在左侧,因此不用成员重载。
    本质:m1.operator<<(cout) // 使用:m1 << cout
  2. 利用全局函数实现左移运算符重载
    本质:operator<<(cout, m1) // 使用 cout << m1
    注意:返回类型为:ostream& ,是引用,否则无法实现链式编程 cout << m1 << endl
  3. 通过以上两点,所以使用全局函数实现加号运算符重载
    如果成员变量为私有的,将此函数设为友元。

#include <iostream>
using namespace std;

class Myint
{
    friend ostream& operator<<(ostream &cout, const Myint &m1);
public:
    Myint(){}

    Myint(int a)
    {   
        this->m_A = a;
    }   

    //试图利用成员函数做<<重载
    //ostream& operator<<(ostream &cout)    // (*this).operator<<(cout)    (*this)<<cout
    //{}

private:
    int m_A;
};

// 利用全局函数和友元实现左移运算符重载
ostream& operator<<(ostream &cout, const Myint &m1)
{
    cout << m1.m_A;
    return cout;
}

int main()
{
    Myint a(20);

    cout << a << endl;
}


1.3 前置后置递增运算符重载

前置运算符重载:T& operator++()
后置运算符重载:T operator++(int)

  1. 前置后置递增运算符重载通过形参中占位参数区分
  2. 前置++ 效率高于 后置++ 效率 ,因为后置++会调用拷贝构造,创建新的数据。

#include <iostream>
using namespace std;

class MyInt
{
    friend ostream& operator<<(ostream &cout, const MyInt &myint);
public:
    MyInt(){}
    // 有参构造
    MyInt(int m)
    {   
        this->m_A = m;
    }   

    // 前置++
    MyInt& operator++()  // 返回类型为MyInt&,可以++(++MyInt)
    {   
        // 先加1
        ++this->m_A;
        // 后返回
        return *this;
    }   
    
    // 后置++
    MyInt operator++(int)    // 返回类型为MyInt,不能 (MyInt++)++
    {   
        // 先保存原始数据
        MyInt old(*this);
        // 再加1
        ++this->m_A;
        // 再返回原始数据
        return old;
    }   
    
private:
    int m_A;
};  

// 如果第二个形参不加const修饰会报错
// 此行:cout << m++ << endl; 
// 错误:无法将左值‘std::ostream {aka std::basic_ostream<char>}’绑定到‘std::basic_ostream<char>&&’
ostream& operator<<(ostream &cout, const MyInt &myint)
{
    cout << myint.m_A;
    return cout;
}

int main()
{
    MyInt m(10);
    cout << m << endl;          // 10

    // 后置++
    cout << m++ << endl;        // 10

    // 前置++
    cout << ++(++m) << endl;    // 13
}


1.4 指针运算符重载

// 重载->运算符
T* operator->()
{
  return T的指针;
}
// 重载 * 运算符
Person& operator*()
{   
    return *(T的指针);
}

/*
智能指针
用途: 托管new出来的对象的释放
设计smartPoint智能指针类,内部维护 Person * ,在析构时候释放堆区new出来的person对象
重载  ->   * 让  sp智能指针用起来向真正的指针
*/

#include <iostream>
using namespace std;

class Person
{
public:
    Person(){}
    Person(int age)
    {   
        this->m_Age = age;
    }   

    void showAge()
    {   
        cout << "年龄为: "<< this->m_Age << endl;
    }   

    ~Person()
    {}   

    int m_Age;
};

class smartPoint
{
public:
    smartPoint(Person *p)
    {  
       this->m_Ptr = p;
    }
    
    //重载->运算符
    Person * operator->()
    {   
        return this->m_Ptr;
    }
    
    //重载 * 运算符
    Person& operator*()
    {   
        return *(this->m_Ptr);
    }
    
    // 析构函数
    ~smartPoint()
    {   
        if (this->m_Ptr != NULL)
        {   
            delete this->m_Ptr;
            this->m_Ptr = NULL;
        }
    }
        
private:
    Person *m_Ptr;
};

int main()
{
    // 利用智能指针 管理 new 出来的对内存
    // sp为栈内存,出了此作用域会释放然后调用smartPoint的析构函数,此析构函数会释放Person堆内存,以此来实现智能指针管理
    smartPoint sp(new Person(10));  // Person *p = new Person(10); smartPoint sp(p);

    sp->showAge();

    (*sp).showAge();

    return 0;
}


1.5 赋值运算符重载

T& operator=( const T &t)

注意事项:如果开辟了堆内存,不仅要实现拷贝构造的深拷贝,同时也要实现赋值运算符重载的深拷贝。

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

//编译器 默认给一个类4个函数   默认构造   析构   拷贝构造 (值拷贝)  operator= (值拷贝)
class Person
{
public:
    Person(char * name, int age)
    {   
        this->m_Name = new char[strlen(name) + 1]; 
        strcpy(this->m_Name, name);
        this->m_Age = age;
    }   

    // 重载=运算符
    Person& operator=( const Person &p) 
    {   
        //先判断原来堆区释放有内容,如果有先释放
        if (this->m_Name != NULL)
        {   
            delete [] this->m_Name;
            this->m_Name = NULL;
        }   

        this->m_Name = new char[strlen(p.m_Name) + 1]; 
        strcpy(this->m_Name, p.m_Name);
        this->m_Age = p.m_Age;
        return *this;
    }   

    // 拷贝构造
    Person(const Person & p)
    {
        this->m_Name = new char[strlen(p.m_Name) + 1];
        strcpy(this->m_Name, p.m_Name);
        this->m_Age = p.m_Age;
    }

    ~Person()
    {
        if (this->m_Name!=NULL)
        {
            delete [] this->m_Name;
            this->m_Name = NULL;
        }
    }

    char * m_Name;
    int m_Age;
};

int main()
{
    Person p1("Tom",10);

    Person p2("Jerry",19);
    p2 = p1;			// 调用=运算符重载的函数

    Person p3("", 0);
    p3 = p2 = p1;		// 调用=运算符重载的函数

	// Person p4 = p3;	// 此处调用拷贝构造,而不是=赋值

    cout << "p1姓名: "<< p1.m_Name << "  p1年龄: " << p1.m_Age << endl;
    cout << "p2姓名: "<< p2.m_Name << "  p2年龄: " << p2.m_Age << endl;
    cout << "p3姓名: "<< p3.m_Name << "  p3年龄: " << p3.m_Age << endl;
}


1.6 []运算符重载

T& operator[](unsigned int index) T 为int、char等等

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

class MyIntArr
{
public:
    MyIntArr(){}
    MyIntArr(unsigned int size)
    {   
        this->m_pAdd = new int[size];
        this->m_Size = size;
    }   
    MyIntArr(const MyIntArr& arr)
    {   
        this->m_pAdd = new int[arr.m_Size];
        memcpy(this->m_pAdd, arr.m_pAdd, arr.m_Size);
        this->m_Size = arr.m_Size;
    }   

    // []运算符重载  注意:返回引用,才能作为左值,进行赋值操作 arr[1] = 100;
    int& operator[](unsigned int index)
    {   
        return this->m_pAdd[index];
    }   

    ~MyIntArr()
    {   
        if (m_pAdd !=  NULL)
        {   
            delete [] m_pAdd;
            m_pAdd = NULL;
        }   
    }   
    int *m_pAdd;		// 在堆区开辟的数组指针
    size_t m_Size;
};

int main()
{
    MyIntArr arr(10);
    for(int ii = 0; ii < 10; ++ii)
    {
        //*(arr.m_pAdd+ii) = ii+1;
        arr[ii] = ii+1;	// 使用重载的[]运算符做赋值操作
    }

    /*
    for(int ii = 0; ii < 10; ++ii)
    {
       cout <<  *(arr.m_pAdd+ii) << endl;
    }
    cout << "-------------" << endl;
    */

    for(int ii = 0; ii < 10; ++ii)
    {
       cout <<  arr[ii] << endl;	// 使用重载的[]运算符
    }

    return 0;
}



1.7 关系运算符重载

bool operator 关系运算符 (const T& t)

#include <iostream>
using namespace std;

class Person
{
public:
    Person(){}
    Person(string name, int age)
    {   
        this->m_Name = name;
        this->m_Age = age;
    }         
    // 重载==关系运算符
    bool operator==(const Person& p)
    {   
        if (this->m_Name == p.m_Name && this->m_Age == p.m_Age)
        {   
            return true;
        }   
        else
        {   
            return false;
        }   
    }   
    // 重载!=关系运算符
    bool operator!=(const Person& p)
    {
        if (this->m_Name == p.m_Name && this->m_Age == p.m_Age)
        {
            return false;
        }
        else
        {
            return true;
        }
    }

    string m_Name;
    int m_Age;
};

int main()
{
    Person p1("张三", 18);
    Person p2("张三", 18);
    Person p3("张三", 20);

    if (p1 == p1)
    {
        cout << "p1 == p2" << endl;
    }
    else
    {
        cout << "p1 != p2" << endl;
    }

    if (p1 == p3)
    {
        cout << "p1 == p3" << endl;
    }
    else
    {
        cout << "p1 != p3" << endl;
    }
}

1.8 函数调用运算符重载 [ ()运算符重载–仿函数 ]

重载()运算符

void operator()(string text)
int operator()(int a,int b)

仿函数写法不固定,比较灵活

#include<iostream>
using namespace std;

class MyPrint
{
public:
    // 重载()运算符
    void operator()(string text)
    {   
        cout << text << endl;
    }   
};

class MyAdd
{
public:
    // 重载()运算符
    int operator()(int a,int b)
    {   
        return a + b;
    }   
};

void MyPrint2(string str)
{
    cout << str << endl;
}

int main()
{

    MyPrint myPrint;
    myPrint("仿函数:hello world"); // 仿函数  本质是一个对象   函数对象

    MyPrint2("普通函数:hello world"); //普通函数

    MyAdd myAdd;
    cout << myAdd(1, 1) << endl;

    cout << MyAdd()(1, 2) << endl; // 匿名函数对象 特点:当前行执行完立即释放

    return 0;
}


2 运算符重载总结

  • =、[]、() 和 -> 操作符只能通过成员函数进行重载
  • << 和 >> 操作符只能通过全局函数配合友元函数进行重载
  • 不要重载 &&|| 操作符,因为无法实现短路规则

建议:将<< 和 >>写成全局函数,其他可重载的符号写到成员即可

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值