zhy123456 的博客

zhy123456 的博客

一个蒟蒻的 blog

题解 P1601 【A+B Problem(高精)】

posted on 2019-10-07 14:35:10 | under 题解 |

前言

这道题本身并不难,我是想占坑说一说运算符重载的问题。上次我在洛谷网校报了个提高组,老师讲高精的代码里用就到了运算符重载,当时聊天区好多人想学,但是聊天区太小,我写不下 (费马:??),故决定写一篇题解造福社会。

举例

其实我们每天都会遇到运算符重载,只是好多人意识不到。下面我举两例,大家应该就能明白了。

例1

第一个例子是cin>>

这个大家很熟悉了。我们都知道其实>>位运算符,但是为什么和cin连用就成输入了呢?是编输入输出流的人给它赋予了一个新的含义,即一运算符多义。联系一函数多义叫函数重载,一运算符多义就应该叫运算符重载。这是一个非常典型的运算符重载。

例2

第二个例子是string的各种运算。

我们从来没听说过"I AK"+" IOI"之类的朴素的字符串加法(CE警告),但是为什么用string类型就可以了呢?同样是编string的人用运算符重载让本来只能用来加int、double之类的变量(字符串算作常字符数组,不是这里指的变量)的运算符可以加string这种“非原装类型”。

定义

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

——百度百科

意思就是让已有的运算符可以对你写的结构体(或类)进行操作。

前置知识——类

其实在C++里,类和结构体没啥区别(还是有一点点区别的:默认访问属性和默认继承方式不同)。但是很多人用的结构体是C的结构体,并不是C++的结构体,所以有必要介绍一下C++的类比C的结构体强大的部分。但是类的东西太多了,无法在一个题解里写完(写完就出书了),故只介绍最基础的东西。

访问属性

这是结构体中很少考虑的。

访问属性有三种,这里只需要用其中的两种:public(公共),private(私有)。public就是说在类的外面可以访问、修改,而private是不能在类外面访问、修改的(用指针是可以的,因此为了维护类的封装性,应减少使用指针)。

示例:

class pair
{
pulic:
    int first;
private:
    int second;
};
int main()
{
    pair a;
    a.first=2;//正确,类外可以访问、修改类内的公共成员
    a.second=2;//错误(CE),类外不能访问、修改类内的公共成员
    return 0;
}

成员函数

这也是结构体中很少用到的。

既然类里面可能有private成员,那就得想一个办法间接地对它操作,比如说用函数

类里面定义函数和类外没有什么区别,但是可以访问、修改类里的私有成员。

示例:

class count
{
private:
    int n=0;
public:
    int get_n(void)
    {return n;}
    void plus(void)
    {n++;}
};

友元函数

定律总是会被打破的 C++提供了一个方法,使类外的函数也能访问、修改类里的私有成员,就是友元函数。友元的特性要在类内声明(声明时前加关键字friend),定义在类外。具体参考一下示例:

class number
{
private:
    int n;
public:
    friend void change(number a,int x);
};
void change(number a,int x){a.n=x;}

写法

大家应该意识到了,运算符其实就是函数,所以要重载运算符,就是要写一个函数对你写的结构体(或类)进行操作。

这样说大家应该就会写了吧。

不过还要注意函数名:operator运算符名,比如operator+

示例:

class integer
{
private:
    int n;
public:
    friend integer operator+(integer a, integer b);//声明为友元函数,函数名就叫operator+
};
bigint operator+(bigint a, bigint b)
{
    integer c;
    c.n=a.n+b.n;//友元函数可以访问、修改类的私有成员
    return c;
}

注意事项

  1. 重载不是新定义,不能定义新的运算符
  2. 运算符重载不能改变操作数个数(单目运算符还是双目运算符)
  3. 运算符重载不能改变结合性(不能从右向左加)
  4. 运算符重载不能改变优先级(不能先加减后乘除)
  5. 不是所有的运算符都可以重载,具体来说,只有以下运算符可以重载

6.运算符的参数必须得有自定义类型(因为对于原有的类型运算符已经定义过了,重载之后编译器不知道该调用哪一个)

7.'=' '&'运算符不用重载(除非你另有用处),因为系统会帮你重载

8.重载双目运算符时通常重载为友元函数(如果重载为成员函数,第一个参数为(*this),参数列表里只用写第二个参数。但是如果第一个参数是原有类型,就无法重载为成员函数),单目运算符重载为成员函数(参数列表里不用写)

9.'++' '--'运算符重载时注意:参数列表是空的时为前置;若要重载后置,需要在参数列表里写一个int.

示例:

class integer
{
private:
    int n;
public:
    integer operator++(){n++;return*this;}//前置
    integer operator++(int)//后置
    {
        integer a=*this;
        n++;
        return a;
    }
};

重载流运算符

<<运算符已经被重载过n次了,可以用来输出int,long long,short,double,string等多种类型,但是我们自己定义的类型还不能用<<运算符输出,这就需要我们自己重载。因为重载时涉及到流对象,比较特殊,所以特别介绍一下。

流提取运算符

说人话就是>>运算符。

我觉得介绍一下cin的原理很有必要,而且也不难。举例:

int a,b;
cin>>a>>b;

>>运算符是左结合的,所以先计算cin>>a,再计算(cin>>a)>>b。计算cin>>a的时候,作用就是从输入流里提取值,赋给a,返回值是cin。计算(cin>>a)>>b时,因为括号内的返回值是cin,所以相当于计算cin>>b,作用是输入b,返回值还是cin

示例:

#include<iostream>//只需要这一个头文件
class integer
{
private:
    int n;
public:
    friend istream& operator>>(istream& a,integer& b);//[1]
};
istream& operator>>(istream& a,integer& b)
{
    a>>b.n;//[2]
    return a;//[3]
}

信息量有点大,逐行解释一下:

  1. [1]行
    • friend 重载流运算符必须重载为友元函数,因为第一个参数的类在头文件里,硬要重载为成员函数就得写到头文件里去,很麻烦。
    • istream i代表input,输入;stream意思是流。合起来就是输入流。我们熟知的cin就是istream类型的。
    • & 并不是取地址运算符,而是引用(这个就不展开讲了,要不然篇幅太长了,请各位自行百度)。第一个参数是引用的原因是返回值必须是引用,返回的就是第一个参数,所以必须是引用。第二个参数是引用的原因是需要给它赋值,而要想做左值参数就得是引用,这样可以避免cin>>a+b;的存在。
    • 这里写你想输入的东西。
    • a这里是istream&类型的,用法和cin相同。因为cin也定义了,所以用cin替换也行。
  2. 就像刚才说的,返回值是istream&型的。

    重载流插入运算符

    就是<<运算符。

方法和刚才差不多,只是第二个参数不能加引用,否则cout<<a+b就会CE了(我就犯过这个错误,因为书上加了引用,我抄上去,就错了)。

示例:

#include<iostream>
class integer
{
private:
    int n;
public:
    friend ostream& operator>>(ostream& a,integer b);
};
ostream& operator>>(ostream& a,integer b)
{
    a<<b.n;
    return a;
}

最后,AC代码

#include <string>
#include <iostream>
using namespace std;
class bigint
{
private:
    int value[10005];
    short leng;
public:
    bigint(int n);
    bigint();
    friend ostream& operator << (std::ostream& output, bigint c);
    friend istream& operator >> (std::istream&, bigint&);
    friend bigint operator+(bigint a, bigint b);
};
int main()
{
    bigint a,b;
    cin>>a>>b;
    cout<<a+b<<endl;
    return 0;
}
bigint::bigint(int n)
{
    for (int i = 0; i < 10005; i++)value[i] = 0;
    n = n >= 0 ? n : -n;
    for (leng = 1; n; leng++, n /= 10)value[leng] = n % 10;
    leng--;
}
bigint::bigint()
{
    for (int i = 0; i < 10005; i++)
        value[i] = 0;
    leng = 0;
}
ostream& operator << (ostream& output, bigint c)
{
    if (c.leng == 0)
    {
        cout << 0;
        return output;
    }
    for (int i = c.leng; i > 0; i--)output << c.value[i];
    return output;
}
istream& operator >> (istream& input, bigint& c)
{
    std::string s;
    input >> s;
    int i=s.length(),j = 0;
    c.leng = s.length();
    for (; i > 0; i--, j++)c.value[i] = s[j] - '0';
    return input;
}
bigint operator+(bigint a,bigint b)
{
    bigint c;
    int max;
    if (a.leng > b.leng)max = a.leng;
    else max = b.leng;
    for (int i = 1; i <= max; i++)
    {
        c.value[i] += a.value[i] + b.value[i];
        c.value[i + 1] = c.value[i] / 10;
        c.value[i] %= 10;
    }
    if (c.value[max + 1] == 0)c.leng = max;
    else c.leng = max + 1;
    return c;
}