C++之运算符重载

之前说过重载,这里仔细说说。

C++引入引用的概念后, 表达式可以被赋值的现象就出现了, 有的表达式可以被赋值,有的表达式则不可以。
比如, int a; int b; int c; (a=b)= c; 是可以的。 而, (a+b) =c ;则是不允许的。 重载的运算符是否会导致表达式可以被赋值应该以基础类型为准。 返回类型通过加 const 加以限定来实现。并不是所有的表达式均可被赋值。

1、重载规则

可被重载的操作符

+-*/%^&\ ~
!,=<>>=<=++--
<<>>==!=&&\\ +=-=/=
%=^=&=\=*=<<=>>=[]()
->->*newnew[]deletedelete[]

不可重载的运算符

.成员访问运算符
.*成员指针访问运算符
::域运算符
sizeof长度运算符
?:条件运算符

只能重载为成员的运算符

=赋值运算符
[]下标运算符
()函数运算符
->间接成员访问
->*间接取值访问

只能对已有的 C++运算符进行重载。

例如, 有人觉得 BASIC 中用"* *"作为幂运算符很方便, 也想在 C++中将"* *"定义为幂运算符, 用"3* *5"表示 35, 这是不行的。

不能改变操作数的个数

关系运算符">"和"<"等是双目运算符, 重载后仍为双目运算符, 需要两个参数。 运算符"+", "-", "*", "&"等既可以作为单目运算符, 也可以作为双目运算符, 可以分别将它们重载为单目运算符或双目运算符。

不改变语义

应当使重载运算符的功能类似于该运算符作用于标准类型数据时候时所实现的功能。例如, 我们会去重载"+"以实现对象的相加, 而不会去重载"+"以实现对象相减的功能,因为这样不符合我们对"+"原来的认知。

至少有一个操作数是自定义类

重载的运算符必须和用户定义的自定义类型的对象一起使用, 其参数至少应有一个 是类对象(或类对象的引用)。也就是说, 参数不能全部是 C++的标准类型, 以防止用户修改用于标准类型数据成员的运算符的性质

不能改变运算符的优先级

重载不能改变运算符的结合性

重载运算符的函数不能有默认的参数

2、operator++()

其他的运算符重载都还好说,但是这个++运算符,分为前++和后++,所以有必要说一下。

前++:

成员函数的重载: 函数类型& operator++()

友元函数的重载:friend 函数类型& operator++(类类型& )

#include <iostream>
using namespace std;
class Complex
{ 
public:
    Complex(float x=0, float y=0)
        :_x(x),_y(y){}
    void dis()
    {
        cout<<"("<<_x<<","<<_y<<")"<<endl;
    }

    friend Complex & operator++(Complex& c);
    
private:
    float _x;
    float _y;
};

Complex & operator++(Complex& c)
{
    c._x++;
    c._y++;
    return c;
}

后++:

成员函数的重载:函数类型 operator++(int)

友元函数的重载:friend 函数类型 operator++(类类型&, int)

为了区别前加加和后加加, 引入了哑元的概念, 引入哑元(增加了入参的方式, 在调用时并不要传任何的参数), 仅仅为了区分, 并无其它意义。

需要注意的一点是,后置++返回的是右值,所以返回类型不要再加&引用了。友元函数的重载和普通函数的重载是一样的。

using namespace std;
class Complex
{ 
public:
    Complex(float x=0, float y=0)
        :_x(x),_y(y){}
    void dis()
    {
        cout<<"("<<_x<<","<<_y<<")"<<endl;
    }
    friend const Complex operator++(Complex &c,int);
private:
    float _x;
    float _y;
};
const Complex operator++(Complex &c,int)
{
    Complex t(c._x,c._y);
    c._x++;
    c._y++;
    return t;
}

3、自定义类型-转化构造函数

实现其它类型到本类类型的转化。

语法:

class 目标类
{
    目标类(const 源类型 & 源类对象引用)
    {
        根据需求完成从源类型到目标类型的转换
    }

};

原理 :转换构造函数, 本质是一个构造函数。 是只有一个参数的构造函数。 如有多个参数, 只能称为构造函数, 而不是转换函数。 转换构造, 强调的是一转一。

应用 :用于传参或是作返回

#include <iostream>
#include <stdlib.h>
#include <time.h>
using namespace std;
//转化构造函数的本质, 也是构造函数
class Point2D
{
    friend class Point3D;
public:
    Point2D(int x=0, int y=0 )
        :_x(x),_y(y){}
private:
    int _x;
    int _y;
};

class Point3D
{ 
public:
    Point3D(int x=0, int y=0 ,int z=0)
        :_x(x),_y(y),_z(z){}
    Point3D(const Point2D & d2)//自定义类型-转化构造函数
    {
        this->_x = d2._x;
        this->_y = d2._y;
        this->_z = rand()%100;
    } 
    void dumpFormat()
    {
        cout<<"("<<_x<<","<<_y<<","<<_z<<")"<<endl;
    }
private:
    int _x;
    int _y;
    int _z;
};

void foo(Point3D d3)
{
    d3.dumpFormat();
} 

int main()
{
    srand(time(NULL));
    Point2D d2(10,100);
    Point3D d3 = d2;
    d3.dumpFormat();
    foo(d2);
    return 0;
}

explicit 关键字

关键字 explicit 可以禁止"单参数构造函数"被用于自动类型转换。 即 explicit 仅用于单参构造(默认参数构成的单参亦算)。转化多是刻意而为之, 以隐式的形式发生, 为了示意同正常构造的不同, 常用 explicti关键字修饰, 要求在转化时显示的调用其构造器完成转化。

//上面代码中的这行前边加上这个关键字之后
explicit Point3D(const Point2D & d2);
//在main函数中
int main()
{
    Point2D d2(10,100);
    Point3D d3 = d2;// 加 explicit 后 编不过
    return 0;
}

自定义类型-操作符函数转化

语法

class 源类
{
    operator 目标类(void)
    {
        return 目标类构造器(源类实参);
    }
}

转换函数必须是类方法, 转换函数无参数, 无返回。

4、仿函数 Functor

把类对象, 像函数名一样使用, 所认称为仿函数, 本质是类对象。

仿函数(functor), 就是使一个类的使用看上去像一个函数。 其实现就是类中实现一个operator(), 这个类就有了类似函数的行为, 就是一个仿函数类了。

class Pow
{ 
public:
    int operator()(int i)
    {
        return i*i;
    } 
    double operator ()(double d)
    {
        return d*d;
    }
};
int main()
{
    Pow pow;
    int i = pow(4);//pow.opreator()(4); pow()4;
    return 0;
}

5、再论默认

在 C++中声明自定义的类, 编译器会默认帮助程序员生成一些, 他们自己未定义的成员函数, 这样的函数版本被称为"默认函数"。

默认函数有哪些?

  • 构造函数
  • 拷贝构造函数
  • 拷贝赋值函数
  • 析构函数

默认重载有哪些?

此外, C++编译器还会为以下这些自定义类型提供全局默认操作符函数:

operator,
operator&
operator&&
operator*
operator->
operator->*
operator new
operator delete

默认的消存

在 C++语言规则中, 一旦程序员自实现了这些函数的自定义版本, 则编译器不会再为该类自动生成默认版本, 最常见的情形, 声明了带参数的构造版本, 则必须声明不带参数的版本以完成无参的完变量的初始化。

Last modification:August 7th, 2019 at 09:00 pm
如果觉得我的文章对你有用,请随意赞赏

Leave a Comment

One comment

  1. 半叶子

    不错,学习了。