(C++) 基于重载运算符的自动取模

直接上代码

// #define int long long

#ifndef MOD
#define MOD 1e9+7
class Num {
private:
    static const int mod = MOD;
    int val = 0;

protected:
    static inline int norm(int x) {
        if (x < 0) {
            int cnt = abs(x)/mod + 1;
            x += mod*cnt;
        }
        return x%mod;
    }

    static int binPow(int base, int expo, int p) {
        if (expo == 0) {    // 防止 mod 1
            return 1 % p;
        }
        base %= p;
        int half = binPow(base, expo>>1, p);
        if (expo & 1) {
            return half*half%p * base%p;
        } else {
            return half*half%p;
        }
    }

public:
    Num() {}
    Num(int val):val(norm(val)) {}
    Num(const Num& num) {
        this->val = num.val;
    }

    ~Num() {}

    int getVal() {
        return this->val;
    }
    void setVal(const int val) {
        this->val = norm(val);
    }

    int& operator()() {
        return this->val;
    }

public:
    friend std::istream& operator>>(std::istream& cin, Num& num) {
        cin >> num.val;
        num.val = num.norm(num.val);
        return cin;
    }
    friend std::ostream& operator<<(std::ostream& cout, Num& num) {
        cout << num.val;
        return cout;
    }
    friend std::ostream& operator<<(std::ostream& cout, Num&& num) {
        cout << num.val;
        return cout;
    }

public:
    Num& operator+=(const Num& rhs) {
        val = norm(val + rhs.val);
        return *this;
    }
    Num& operator-=(const Num& rhs) {
        val = norm(val - rhs.val);
        return *this;
    }
    Num& operator*=(const Num& rhs) {
        val = norm(val * rhs.val);
        return *this;
    }
    Num& operator/=(const Num& rhs) {
        int inverseElement = binPow(rhs.val, mod-2, mod);
        val = norm(val * inverseElement);
        return *this;
    }

public:
    Num operator+() {
        Num tmp = val;
        return tmp;
    }
    Num operator-() {
        Num tmp = -val;
        return tmp;
    }

    friend Num operator+(const Num& lhs, const Num& rhs) {
        Num tmp = lhs;
        tmp += rhs;
        return tmp;
    }
    friend Num operator-(const Num& lhs, const Num& rhs) {
        Num tmp = lhs;
        tmp -= rhs;
        return tmp;
    }
    friend Num operator*(const Num& lhs, const Num& rhs) {
        Num tmp = lhs;
        tmp *= rhs;
        return tmp;
    }
    friend Num operator/(const Num& lhs, const Num& rhs) {
        Num tmp = lhs;
        tmp /= rhs;
        return tmp;
    }

public:
    Num operator++(signed) {   // 后置
        Num tmp = *this;
        val = norm(val+1);
        return tmp;
    }
    Num operator--(signed) {   // 后置
        Num tmp = *this;
        val = norm(val-1);
        return tmp;
    }
    Num& operator++() {   // 前置
        val = norm(val+1);
        return *this;
    }
    Num& operator--() {   // 前置
        val = norm(val-1);
        return *this;
    }

public:
    bool operator!() const {
        return val == 0;
    }
    bool operator==(const Num& rhs) const {
        return val == rhs.val;
    }
    bool operator!=(const Num& rhs) const {
        return val != rhs.val;
    }
    bool operator>(const Num& rhs) const {
        return val > rhs.val;
    }
    bool operator>=(const Num& rhs) const {
        return val >= rhs.val;
    }
    bool operator<(const Num& rhs) const {
        return val < rhs.val;
    }
    bool operator<=(const Num& rhs) const {
        return val <= rhs.val;
    }
    bool operator&&(const Num& rhs) const {
        return val && rhs.val;
    }
    bool operator||(const Num& rhs) const {
        return val || rhs.val;
    }

};
#endif // MOD

前言

在一些题目或情景中,需要我们对一个数进行取模运算,但是每次手动写取模会比较麻烦,因此就有了本文。

在C++,C#等语言中支持重载运算符,那么我们就可以自己写一个工具类,来满足自动取模的要求。

各部分介绍

宏定义

定义一个用来取模的宏 MOD 定义的时候可以灵活一点,按照需求定义

注意有时在算法竞赛中会习惯无脑开long long

这里和上文虽然注释了,但在实际中最好默认开启,防止乘法直接溢出

本文默认开启了long long

// #define int long long

#ifndef MOD
#define MOD 1e9+7
class Num {};
#endif // MOD

成员变量

mod 可以定义为静态常量

val 设不设初始值其实都可以

private:
    static const int mod = MOD;
    int val = 0;

辅助函数

static 静态成员方法,所有对象公用一个

inline 内联函数,小代码展开

static inline int norm(int x);

norm函数保证数值在每次变化时自动计算取模,不然每次重载也要手动写取模也会比较麻烦

static int binPow(int base, int expo, int p);

binPow快速幂,在除法取模中需要用到。这个涉及数论的知识,不过多介绍。

protected:
    static inline int norm(int x) {
        if (x < 0) {
            int cnt = abs(x)/mod + 1;
            x += mod*cnt;
        }
        return x%mod;
    }

    static int binPow(int base, int expo, int p) {
        if (expo == 0) {    // 防止 mod 1
            return 1 % p;
        }
        base %= p;
        int half = binPow(base, expo>>1, p);
        if (expo & 1) {
            return half*half%p * base%p;
        } else {
            return half*half%p;
        }
    }

构造和析构

注意:

拷贝构造的时候一定要传引用,否则会无限递归,在编译时还不会警告

普通构造要调用norm(),下文不再多次强调norm()

public:
    Num() {}
    Num(int val):val(norm(val)) {}
    Num(const Num& num) {
        this->val = num.val;
    }

    ~Num() {}

空类的默认成员函数

  1. 缺省构造函数 (无参)
  2. 拷贝构造函数
  3. 析构函数
  4. 赋值运算符
  5. 取址运算符
  6. 取值运算符const

get/set

其实用的不多,但为了规范就写上了

其实可以等号直接赋值,但用等号会强制类型转换,生成一个临时变量

public:
    int getVal() {
        return this->val;
    }
    void setVal(const int val) {
        this->val = norm(val);
    }

仿函数

目的是为了方便快速直接获取值

在一些必要情况下,如:数组下标等

同时解决上面getVal()返回的是普通值而不是引用的情况。

public:
    int& operator()() {
        return this->val;
    }

标准输入输出

注意:

一个cpp程序只有一个cin,cout 因此必须传入引用,且为了达到链式编程的效果,需要重新传引用回去

friend std::ostream& operator<<(std::ostream& cout, Num num)

时为了避免频繁的创造临时变量,可以分成两个左值和右值都写一下。

public:
    friend std::istream& operator>>(std::istream& cin, Num& num) {
        cin >> num.val;
        num.val = num.norm(num.val);
        return cin;
    }
    friend std::ostream& operator<<(std::ostream& cout, Num& num) {
        cout << num.val;
        return cout;
    }
    friend std::ostream& operator<<(std::ostream& cout, Num&& num) {
        cout << num.val;
        return cout;
    }

重载+= -= *= /=

由于这是对操作符左边的本身的操作,因此需要返回本身。

这里可以用到this指针,取其值并用返回引用

return *this;

public:
    Num& operator+=(const Num& rhs) {
        val = norm(val + rhs.val);
        return *this;
    }
    Num& operator-=(const Num& rhs) {
        val = norm(val - rhs.val);
        return *this;
    }
    Num& operator*=(const Num& rhs) {
        val = norm(val * rhs.val);
        return *this;
    }
    Num& operator/=(const Num& rhs) {
        int inverseElement = binPow(rhs.val, mod-2, mod);
        val = norm(val * inverseElement);
        return *this;
    }

重载+ - * /

无参的表示正负号,其实用处不大

注意普通的±*/是生成一个临时的右值,且不会对数值本身有任何改变,因此我们临时创建一个变量


由于二元运算符的重载只能有一个参数或者无参

因此有两个参数时只能定义为友元函数

public:
    Num operator+() {
        Num tmp = val;
        return tmp;
    }
    Num operator-() {
        Num tmp = -val;
        return tmp;
    }

    friend Num operator+(const Num& lhs, const Num& rhs) {
        Num tmp = lhs;
        tmp += rhs;
        return tmp;
    }
    friend Num operator-(const Num& lhs, const Num& rhs) {
        Num tmp = lhs;
        tmp -= rhs;
        return tmp;
    }
    friend Num operator*(const Num& lhs, const Num& rhs) {
        Num tmp = lhs;
        tmp *= rhs;
        return tmp;
    }
    friend Num operator/(const Num& lhs, const Num& rhs) {
        Num tmp = lhs;
        tmp /= rhs;
        return tmp;
    }

重载自增,自减运算符++ --

注意前置和后置的区别

++i 先加后用 需要返回引用,链式的思想

i++ 先用后加 是一个临时值

重载后置时,需要用一个int占位符表示

由于#define int long long的存在

这里的占位符可以写成signed

public:
    Num operator++(signed) {   // 后置
        Num tmp = *this;
        val = norm(val+1);
        return tmp;
    }
    Num operator--(signed) {   // 后置
        Num tmp = *this;
        val = norm(val-1);
        return tmp;
    }
    Num& operator++() {   // 前置
        val = norm(val+1);
        return *this;
    }
    Num& operator--() {   // 前置
        val = norm(val-1);
        return *this;
    }

重载比较、逻辑运算符

注意:

在重载比较运算符的时候最好将函数写成const,否则会在一些#include<algorithm>的算法中会报错

且这些函数均是返回布尔值,不会改变内部的成员变量,因此全写成const

public:
    bool operator!() const {
        return val == 0;
    }
    bool operator==(const Num& rhs) const {
        return val == rhs.val;
    }
    bool operator!=(const Num& rhs) const {
        return val != rhs.val;
    }
    bool operator>(const Num& rhs) const {
        return val > rhs.val;
    }
    bool operator>=(const Num& rhs) const {
        return val >= rhs.val;
    }
    bool operator<(const Num& rhs) const {
        return val < rhs.val;
    }
    bool operator<=(const Num& rhs) const {
        return val <= rhs.val;
    }
    bool operator&&(const Num& rhs) const {
        return val && rhs.val;
    }
    bool operator||(const Num& rhs) const {
        return val || rhs.val;
    }

一些使用注意点

Num 本质是一个类

不能直接在if等语句中用非0即1的思想,要像java一样老老实实的用符号判断出布尔值

不能直接写成数组下标,可以用getVal()或者仿函数来写


和普通数值进行运算的时候,一定要注意:必须Num 左, 普通数值 右

若想要 普通数值 左, Num 右

则需要以友元函数的形式重载操作符,上文代码中未写出(又要写一堆函数,代码量太大了)

signed main () {

    Num num = 100;

    // 普通数值在左边不行
    // if (0 < num)
    if (num > 0) {
        vector<Num> arr(num()+1);
        /**
        	num * 114514
        	这里实际上是进行了类型转化
        	先将114514转化成Num类型的数据,再计算(if 中的 num > 0 也是)
        	可以在构造和析构中输出检验
        */ 
        iota(arr.begin(), arr.end(), num * 114514);
        cout << arr[num.getVal()] << endl;
    }

    system("pause");
    return 0;
}



END

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值