實現動態大數結構

ChebyshevTST發表於2023-10-27

  大數結構是一種常見的資料結構,在C++當中,我們常用vector來動態實現。除此之外,我們也可以仿照vector的思路,自己實現記憶體的動態分配,當記憶體容量達到上限時,用C-api realloc進行記憶體的重新分配。

#define REQUIRE2(p, q) assert((p) || (q))
#define REQUIRE1(p) assert(p)
#define INITSIZE 24

  先定義了幾個宏,前兩個是對錶達式的判斷,INITSIZE是動態大數結構的初始閾值,當超過了這個閾值之後,便發生擴容。

public:
        BigNum(const BigNum& other) : sign(other.sign), p(other.p) {}
        BigNum() {
            p = static_cast<char *>(malloc(INITSIZE));
            REQUIRE1(p);
        }
        BigNum(BigNum&& other) : sign(other.sign), p(other.p) {}
        ~BigNum() {
            free(p);
        }

        BigNum& operator=(const BigNum& other) = default;
        bool operator==(const BigNum& other) const = default;

  構造析構......注意析構要釋放記憶體,防止發生記憶體洩漏。

void init() {
            sign = getchar();
            REQUIRE2(sign == '+', sign == '-');
            
            char c;
            char *start = p;
            while ((c = getchar()) != ' ' && c != '\n' && c != '\t' && c != '\r') {
                REQUIRE1(isdigit(c));
                if (start - p >= threshold - 1) {
                    void *mem = realloc(p, threshold << 1);
                    REQUIRE1(mem);
                    p = static_cast<char *>(mem);
                    start = p + threshold - 1;
                    threshold <<= 1;
                }
                *start = c;
                ++start;
            }
            start = nullptr;
        }

  這是關鍵的一部分,透過getchar函式,使得從鍵盤上動態讀取字元,並且維護一個指標start,這樣就可以實時統計大數結構當中的容量。在這裡,前面定義的宏就派上用場了,需要嚴格檢查讀入的字元是否為數字(第一個讀入的字元必須是+或者-符號)。

friend std::ostream& operator<<(std::ostream& os, const BigNum& big) {
            os << big.sign << big.p;
            return os;
        }

  過載輸出函式。

private:
        char sign;
        char *p;
        std::size_t threshold = INITSIZE;

  在內部,維護了三個成員,分別是數的符號,指向初始記憶體位置的指標與閾值(閾值每次發生擴容時會乘2)。

 

  完整程式碼如下

#define REQUIRE2(p, q) assert((p) || (q))
#define REQUIRE1(p) assert(p)
#define INITSIZE 24

class BigNum {
    public:
        BigNum(const BigNum& other) : sign(other.sign), p(other.p) {}
        BigNum() {
            p = static_cast<char *>(malloc(INITSIZE));
            REQUIRE1(p);
        }
        BigNum(BigNum&& other) : sign(other.sign), p(other.p) {}
        ~BigNum() {
            free(p);
        }

        BigNum& operator=(const BigNum& other) = default;
        bool operator==(const BigNum& other) const = default;

        void init() {
            sign = getchar();
            REQUIRE2(sign == '+', sign == '-');
            
            char c;
            char *start = p;
            while ((c = getchar()) != ' ' && c != '\n' && c != '\t' && c != '\r') {
                REQUIRE1(isdigit(c));
                if (start - p >= threshold - 1) {
                    void *mem = realloc(p, threshold << 1);
                    REQUIRE1(mem);
                    p = static_cast<char *>(mem);
                    start = p + threshold - 1;
                    threshold <<= 1;
                }
                *start = c;
                ++start;
            }
            start = nullptr;
        }

        friend std::ostream& operator<<(std::ostream& os, const BigNum& big) {
            os << big.sign << big.p;
            return os;
        }

    private:
        char sign;
        char *p;
        std::size_t threshold = INITSIZE;
};

 

相關文章