C++操作符重载相关知识


一、操作符函数重载

什么是操作符函数:在C++中针对类类型的对象的运算符,由于它们肯定不支持真正的运算操作,因此编译器会将它们翻译成函数,这种就叫做操作符函数(运算符函数)。
编译器把运算翻译成运算符函数,可以针对自定义的类类型设计它独有的运算功能。
其实各种运算符已经具备一些功能,再次实现它的就是叫作运算符重载。

双目运算符:
    a+b
    成员函数
        a.operator+(b);
    全局函数
        operator+(a,b);
单目运算符:
    !a
    成员函数
        a.operator!(void);
    全局函数
        operator!(a);

二、双目操作符函数重载

成员函数:
const 类对象 operator#(const 类& that) const
{
    return 类(参数#参数);
}
注意:双目录运算符的运算结果是个右值,返回值应该加 const ,然后为了const对象能够调用,参数应写const,函数也应该具备const属性。

全局函数:
const 类 operator#(const 类& a,const 类& b)
{

}
注意:全局函数不是成员函数,可能会需要访问到类的私有成员,解决这种问题可以把函数声明为类的友元函数(友元不是成员)。
友元:在类的外部想访问类的私有成员(public/protected/private)时,需要把所在的函数声明为友元,但是友元只是朋友,因此它只有访问权,没有实际的拥有权(其根本原因是它没有this指针)。
友元声明:把函数的声明写一份到类中,然后在声明前加上friend 关键字。使用友元即可把操作符函数定义为全局的,也可以确保类的封装性。
注意:友元函数与成员函数不会构成重载关系,因此它们不在同一个作用域内。

三、赋值类型的双目操作符

成员
    类 operator#(void)
    {

    }
全局
    类 operator#(const 类& that)
    {

    }
1、获取单参构造成赋值运算的调用方式。
String str = "xxx"; // 会调用单参构造,而不调用赋值运算符
str = "hhh";

2、左操作数据不能具有const属性
    1.成员函数不能是常函数
    2.全局函数第一个参数不能有const属性
3、返回值应该都(成员/全局)具备const属性

四、单目操作符函数重载

-,~,!,&,*,->,++,--
成员
    const 类 operator#(void) const
    {

    }
全局
    const 类 operator#(const 类& that)
    {

    }
前++/--
    类& operator#(void)
    {

    }
    类& operator#(类& that)
    {

    }
后++/--(哑元)
    const 类& operator#(int)
    {

    }
    const 类& operator#(类& that,int)
    {

    }

五、输入输出操作符重载

cout 是 ostream 类型的对象,cin 是 istream 类型的对象。
如果<</>>运算实现为成员函数,那么调用者应该是ostream/istream,而我们无权增加标准库的代码,因此输入/输出运算符只能定义为全局函数。

ostream& operator<<(ostream& os,const 类& n)
{

}
istream& operator>>(istream& os,类& n)
{

}

六、特殊操作符的重载(笔试面试比较重要)

1、下标操作符 [],常用于在容器类型中以下标方式获取元素。
类型& operator[](int i)
{

}
2、函数操作符(),一个类如果重载函数操作符,那么它的对象就可以像函数一样使用,参数的个数、返回值类型,可以不确定,它是唯一一个可以参数有缺省参数的操作符。
#include <iostream>
#include <stdlib.h>
using namespace std;

class Array
{
    int* arr;
    size_t len;
public:
    Array(size_t len):len(len)
    {
        arr = new int[len];
    }

    void operator()(void)
    {
        cout<<"emmm"<<endl;
    }

    int& operator[](int i)
    {
        if(i < 0 || i >= len)
        {
            cout<<"下标错误"<<endl;
            exit(0);
        }
        return arr[i];
    }
};

int main()
{
    Array arr(100);
    for(int i=0; i<10; i++)
    {
        arr[i] = i;
        cout<< arr[i] << endl;
    }
    arr();
}
3、解引用操作符*,成员访问操作符->
    如果一个类重载了*和->,那么它的对象就可以像指针一样使用。
    所谓的智能指针就是一种类对象,它支持解引用和成员访问操作符。
4、智能指针
    常规指针的缺点:
        当一个常规指针离开它的作用域时,只有该指针所占用的空间会被释放,而它指向的内存空间能否被释放就不一定了,在一些特殊情况(人为、业务逻辑特殊)free或delete没有执行,就会形成内存泄漏。
    智能指针的优点:
        智能指针是一个封装了常规指针的类类型对象,当它离开作用域时,它的析构函数会自动执行,它的析构函数会负责释放常规指针所指向的动态内存(以正确方式创建的智能指针,它的析构函数才会正确执行)。
    智能指针和常规指针的相同点:都支持*和->运算。
    智能指针和常规指针的不同点:
        任何时候,一个对象只能使用一个智能指针来指向,而常规指针可以指向多次。
        只能指针的赋值操作需要经过拷贝构造和赋值构造特殊处理(深拷贝)。
#include <iostream>
using namespace std;

class Int
{
public:
    int val;
    Int(int val=0):val(val){ }

    void set_val(int val)
    {
        this->val = val;
    }
    int get_val(void)
    {
        return val;
    }

    Int& operator=(const int val)
    {
        this->val = val;
        return *this;
    }

    ~Int(void)
    {
        cout<<"我是Int的析构函数"<<endl;
    }

    friend ostream& operator>>(ostream& os,Int& n);
};
ostream& operator<<(ostream& os,Int& n)
{
    return os<<n.val;
}

class IntPointer
{
    Int* ptr;
public:
    IntPointer(Int* ptr):ptr(ptr){ }
    Int& operator*(void)
    {
        return *ptr;
    }
    ~IntPointer(void)
    {
        delete ptr;
    }
};

int main()
{
    Int* num =new Int(100);
    IntPointer p = num;
    *p = 20;
    cout<<*p <<endl;
    *p = 10;
    cout<<*p <<endl;
}
auto_ptr:标准库中封装好的智能指针,实现了常规指针的基本功能,头文件 #include <memory>
    用法:auto_ptr<指向的类型> 指针变量名(对象的地址)
    auto_ptr的局限性:
        不能跨作用域使用,一旦离开作用域指针变量会释放它指向的对象也会释放。
        不能放入标准容器。
        不能指向对象数组。
#include <iostream>
#include <memory>
using namespace std;

class A
{
public:
    A(void)
    {
        cout<<"构造"<<endl;
    }

    ~A(void)
    {
        cout<<"析构"<<endl;
    }

    void show(void)
    {
        cout<<"A's show"<<endl;
    }

};

int main()
{
    auto_ptr<A> ptr(new A);
    (*ptr).show();
}
5、new/delete/new[]/delete[]运算符重载
    1.C++缺省的堆内存管理器速度较慢,重载new/delete底层使用malloc/free可以提高运行速度。
    2.new在失败会产生异常,而每次使用new时为了安全都应该进行异常捕获,而重载new操作符只需要在操作符函数中进行一次错误处理即可。
    3.在一些占字节数比较小的类,频繁使用new,可能会产生大量的内存碎片,而重载new操作符后,可以适当的扩大每次申请的字节数,减少内存碎片产生的机率。
    4.重载 new/delete 可以记录堆内存使用的信息
    5.重载 delete 可以检查到释放内存失败时的信息,检查到内存泄漏。

七、重载操作符的限制

1、不能重载的操作符
    域限定符 ::
    直接成员访问操作符 .
    三目操作符 ?:
    字节长度操作符 sizeof
    类型信息操作符 typeid
2、重载操作符不能修改操作符的优先级
3、无法重载所有基本类型的操作符运算
4、不能修改操作符的参数个数
5、不能发明新的操作符

关于操作符重载的建议:

1、在重载操作符时要根据操作符实际的功能和意义来确定具体参数,返回值,是否具有const属性,返回值是否是引用或者临时对象。
2、重载操作符要符合情理(要有意义),要以实际用途为前提。
3、重载操作符的意义是为了让对象的操作更简单、方便,提高代码的可读性,而不是为了炫技。
4、重载操作符要与默认的操作符的功能、运算规则一致,不要出现反人类的操作。
#define ture 0
#define false 1

相关测试代码:

#include <iostream>
using namespace std;

class Point
{
    int x;
    int y;
public:
    Point(int _x=0,int _y=0)
    {
        x = _x;
        y = _y;
    }

/*    void show(void) const
    {
        cout<< "(x:" <<x <<",y:" <<y<< ")"<<endl;
    }*/

    friend const Point operator+(const Point& a,const Point& b);
    friend const Point operator-(const Point& a,const Point& b);
    friend const Point operator*(const Point& a,int b);
    friend const Point operator*(int a,const Point& b);
    const Point operator/(const int that) const
    {
        return Point(x/that,y/that);
    }
/*    const Point operator+(const Point& that) const
    {
        return Point(that.x+x,that.y+y);
    }
*/
    const Point& operator+=(const Point& that)
    {
        x += that.x;
        y += that.y;
        cout<<"___";
        return *this;
    }

    Point operator-(void)
    {
        return Point(-x,-y);
    }

    // 前++
    Point& operator++(void)
    {
        x++;
        y++;
        return *this;
    }

    // 后++ (哑元)
/*    Point operator++(int)
    {
        Point temp(x,y);
        x++;
        y++;
        return temp;
    }
*/
    friend Point operator++(Point& that,int);//友元
    Point operator--(int)
    {
        Point temp(x,y);
        x--;
        y--;
        return temp;
    }
    friend ostream& operator<<(ostream& os,const Point& p);
    friend istream& operator>>(istream& is,Point& p);
};

ostream& operator<<(ostream& os,const Point& p)
{
    return os <<p.x<<","<<p.y;
}

istream& operator>>(istream& is,Point& p)
{
    cout << "请输入x的值:";
    is >> p.x;
    cout<<"请输入y的值:";
    is >> p.y;
    return is;
}

Point operator++(Point& that,int)
{
    Point temp(that.x,that.y);
    that.x++;
    that.y++;
    return temp;
}

const Point operator+(const Point& a,const Point& b)
{
    return Point(a.x+b.x,a.y+b.y);
}

const Point operator-(const Point& a,const Point& b)
{
    return Point(a.x-b.x,a.y-b.y);
}

const Point operator*(const Point& a,int b)
{
    return Point(a.x*b,a.y*b);
}

const Point operator*(int a,const Point& b)
{
    return Point(a*b.x,a*b.y);
}

int main()
{
    Point p(3,9);
    Point p1(1,3);
    Point p2 = p1+p+p1;
    cout<<"+:";
    cout<<p2<<endl;

    Point p4(6,6);
    cin>>p4;
    cout<<p4<<endl;
//    p2.show();
    p2 = p2-p1;
    cout<<"-:";
    cout<<p2<<endl;
//    p2.show();
    p2 = p1*3;
    cout<<"*:";
    cout<<p2<<endl;
//    p2.show();
    p2 = 3*p1;
//    p2.show();
    cout<<p2<<endl;

    cout<<(-p1)<<endl;
//    (-p1).show();

    p2 = p1;
//    p2.show();

//    (++p1).show();
    cout<<(++p1)<<endl;

//    (p2++).show();
    cout<<"p2:"<<(p2++)<<endl;
    cout<<"p2:"<<p2<<endl;
//    p2.show();

    cout<<"p2:"<<(p2--)<<endl;
    cout<<"p2:"<<p2<<endl;
//    (p2--).show();
//    p2.show();

    p2 = p2/3;
//    p2.show();
    cout<<"p2:"<<p2<<endl;

}

如有错误,望指出,谢谢~


Reprint policy: All articles in this blog are used except for special statements CC BY 4.0 reprint polocy. If reproduced, please indicate source Love丶伊卡洛斯 !
评论
 Previous
网络通信TCP/UDP 网络通信TCP/UDP
一、网络通信的基本概念1、TCP和UDP的区别 TCP(Transmission Control Protocol):传输控制协议,面向连接的服务(类似打电话),安全、可靠(三次握手、响应+重传、四次挥手),速度相对较慢,一般应用
Next 
UML相关知识 UML相关知识
一、UML概述1、什么是UML 统一建模语言(Unified Modeling Language)。用于说明、可视化、构建和编写一个正在开发的、面向对象的、软件密集系统的开发方法。 UML展现了一系列最佳工程实践,这些最佳实践在对大
2019-09-04
  TOC