c++静态多态-运算符重载

运算符重载

运算符重载本质上也是一种函数重载,运算符重载主要用于扩展运算符的使用范围,例如,+一般用于数字的加和,类A中包含了int类型的a和b,如果我要让类A的两个对象相加表示类A中的a和b分别相加,然后返回新的对象,那么传统的+无法完成这项任务,就需要重载+运算符,代码如下:

class A
{
public:
    int a, b;
    explicit A(const int a, const int b): a(a), b(b){}
	// 1. 直接返回一个新的拷贝,消耗性能
    A operator+(A &_a) const
    {
        A retA(0, 0);
        retA.a = this->a + _a.a;
        retA.b = this->b + _a.b;
        return retA;
    }
	// 可以直接返回引用
	A& operator+(A &_a) //  2. 此时改变了类的属性值,不用const
	{
		this->a = this->a + _a.a;
		this->b = this->b + _a.b;
		return *this;
	}

};

int main() {
    A a(1,2);
    A b(3,4);
    A c = a + b;
    std::cout << c.a << " " << c.b << std::endl;
    return 0;
}

// 4  6

运算符重载的两种方法

类内成员函数重载

成员函数重载类型可以分为一元运算符和二元运算符
二元运算符重载
即 + - * / 等的重载,例如我们实现 在类之间实现 - ,重载其他的运算符类似

class A
{
public:
    int a, b;
    ...
    A & operator-(A &_a) 
    {
        a = a - _a.a;
        b = b - _a.b;
        return *this;
    }
	...
};

一元运算符重载
例如 ++ -- 等,针对这个运算符,存在前置和后置的概念,对于前置 ++,需要注意是先进行+运算,再返回,因此其代码如下:

A & operator++()
{
    a = a + 1;
    b = b + 1;
    return *this;
}

后置++与前置不同,后置++需要使用int作为函数参数占位符,以此区分前置后置,且前置++返回的是对象的本身,可以返回引用,而后置++是先返回对象,再对对象 ++,故返回的是拷贝。

class A {
public:
	A(const A& a)
	{
	    this->a = a.a;
	    this->b = a.b;
	}
	
	A operator++(int)
	 {
	     A r(*this); // 调用拷贝构造函数
	     // 如果修改成A r = *this则调用拷贝赋值运算符
	     a = a + 1;
	     b = b + 1;
	     return r;
	 }
}

友元函数重载

友元函数不是类的成员函数,但是它能够获取到类的私有属性,可以这么理解,友元函数可以看成是类的好兄弟,类的小秘密(私有属性)能够和好兄弟共享,自己的儿子(继承类)或者其他人都无法知道这个秘密。

友元函数的声明需要在类中表示,即在一个函数前加上friend关键字即可。我们通过友元函数再来实现一下上面的一元二元运算符,并且包括前置++和后置++

class A
{
private:
    int a, b;
public:
    explicit A(int a, int b): a(a), b(b) {}
    ~A(){};
    friend A operator+(A &a, A &b);
    friend void operator++(A &a);
    friend A operator++(A &a, int); // 同样需要使用占位符表示前置和后置
    int getA() {return a;}
    int getB() {return b;}
};

// 类的成员函数此处重载+可以返回,可以不返回,因为成员函数可以获取到this,
// 而友元函数无法获取到this,故需要返回。
A operator+(A &a, A &b)
{
    A c(0, 0);
    c.a = a.a + b.a;
    c.b = a.b + b.b;
    return c;
}
void operator++(A &a)
{
    a.a += 1;
    a.b += 1;
}

A operator++(A &a, int)
{
    A b(a.a, a.b);
    a.a += 1;
    a.b += 1;
    return b;
}

int main() {
    A a(1,2);
    A b(3,4);
    A c = a + b;
    ++ b;
    A d = b++;
    std::cout << c.getA() << " " << c.getB() << std::endl;
    std::cout << b.getA() << " " << b.getB() << std::endl;
    std::cout << d.getA() << " " << d.getB() << std::endl;
    return 0;
}

运算符重载本质上来说也是函数调用

// 我们可以通过下面的方法调用
A c = operator+(a, b); // 效果等同于A c = a + b; 我们更习惯于写成后面这种
A d = func(a, b); // 等同于这种
一些特定的运算符的重载

=:赋值运算符重载,当类中包含动态分配的内存时,必须重载赋值运算符,否则可能出问题。默认的赋值运算符采用的是浅拷贝,即拷贝指针的值,不拷贝指针所指向的值,拷贝之后有两个指针指向同一块儿内存,当主要程序运行结束后,释放内存,当delete 一个指针后,另外一个指针所指向的内存被释放,指向内容不确定,再次delete就不合理。因此针对这种情况,必须重载赋值运算符函数以及拷贝构造函数。
浅拷贝

class B
{
public:
    explicit B(int a, int b) : a(a), b(b)
    {
        c = new int(5);
    }
    ~B() // 析构函数,释放内存
    {
        delete c;
    }
	// 拷贝构造函数
    B(const B & b_)
    {
        this->a = b_.a;
        this->b = b_.b;
        this->c = new int(*b_.c);
    }
	// 拷贝赋值运算符 ,这个是属于=前面的对象的,因此使用this可以操作它
    B& operator=(const B & b_)
    {
        this->a = b_.a;
        this->b = b_.b;
        this->c = new int(*b_.c);
        return *this;
    }


public:
    int a, b;
    int * c;
};

B b(1,2,3);
B c(b); // 拷贝构造函数
B d = b; // 拷贝赋值运算符
std::cout << b.a << " " << b.b << " " << *b.c << std::endl;

[]重载,这个代表数组取下标符号,比较常用的数组,例如vector,array等都可以使用[],因此重载[]可直接取下标非常有必要,以数组为例

class Array
{
public:
    explicit Array(int size): S(size)
    {
        arr = new int[size];
        cur = -1;
    }

    ~Array() // 有动态分配的内存就需要手动delete或者free
    {
        delete [] arr;
    }

    int& operator[](int t)
    {
        assert(t >=0 && t < S);
        return *(arr + t);
    }

    void push_back(int v)
    {
        assert(cur + 1 < S);
        ++ cur;
        arr[cur] = v;
    }
    void pop_back()
    {
        assert(cur > -1);
        -- cur; // 只需要向前 - 1即可
    }

    int getSize()
    {
        return cur + 1;
    }

private:
    int * arr;
    int S;
    int cur;
};

()重载,即函数调用的方法

class C
{
public:
	    double operator()(double a, double b)
	    {
	        return a * b;
	    }
};

注意: = [] ()均不能通过友元函数重载

>> << ,这两个运算符在c++中作为插入运算符和提取运算符,通常在cin和cout中使用。我们可以通过重载这两个运算符用于自定义的类型

class D
{
public:
    // 返回的必须是引用,以完成连续 >> 或者连续 << 的操作
    explicit D(int c, int d): c(c), d(d) {};

    friend std::ostream& operator<<(std::ostream& out, const D &d);

    friend void operator>>(std::istream& in, D &d);

public:
    int c, d;
};

// 输出系列
std::ostream& operator<<(std::ostream& out, const D &d)
{
    out << "D(" << d.c << "," << d.d << ")" << std::endl;
    return out;
}
// 输入系列
void operator>>(std::istream& in, D &d)
{
    in >> d.c >> d.d;
    std::cout << d;
}

? 为什么上述 >> << 的重载需要使用友元函数

因为上述运算符的参数有一个是这个类的对象,但是另外一个并不是这个类的对象

关系运算符的重载

成对出现,例如 > 与 <、>=与<=、==与!=

class E
{
public:
    explicit E(int a, int b) : a(a), b(b) {};
    // == 与!=
    bool operator==(const E &e)
    {
        return (a + b) == (e.a + e.b);
    }

    bool operator!=(const E &e)
    {
        return !(*this == e); // 直接调用上面的==函数
    }

    // > 与 <
    bool operator>(const E &e)
    {
        return (a + b) > (e.a + e.b);
    }

    bool operator<(const E &e)
    {
        return !(*this >= e);
    }

    // >= 与 <=
    bool operator>=(const E &e)
    {
        return (a + b) >= (e.a + e.b);
    }

    bool operator<=(const E &e)
    {
        return !(*this > e);
    }

public:
    int a, b;
};
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值