Bittömb

Czirkos Zoltán · 2022.06.21.

Pakolt tömb indexelése

Ha egy pakolt tömbünk van, ahol egy bájton belül több elemet tárolunk (ilyen például egy bittömb, amelyik minden bitet használ), akkor az egyes elemeket nem tudjuk megcímezni. A legkisebb címezhető memóriaegység ugyanis a char.

Ilyenkor közvetlenül nem lehet rendes indexelő operátort csinálni; a kiválasztott elemnek ugyanis nem tudjuk referenciáját visszaadni sem.

Helyette a lenti megoldás használható. Az indexelő operátor egy kis segédobjektumot ad vissza. Jelen esetben az objektum bool-lá alakító operátora a bitet adja vissza; az operator=(bool)-ja pedig a hivatkozott bitet változtatja.

#include <iostream>
#include <cstdint>

class BitTomb {
  private:
    uint32_t c = 0;     /* legyen most ez a tömb :) */
  
  public:
    BitTomb() = default;
    
    /* Proxy osztályocska, ilyet ad vissza a BitTömb operator [].
     * Ennek két átdefiniált operátora: bool-lá alakítás és bool értékadás. */
    class Proxy {
        /* megjegyzi magának, melyik bittömb melyik indexéről van szó. */
        BitTomb *ezen;
        int index;
    public:
        Proxy(BitTomb *bt, int i): ezen(bt), index(i) {}

        operator bool () const {
            return (ezen->c >> index) & 1;
        }

        Proxy& operator=(bool uj) {         /* bitseged=bool */
            if (uj) {   /* 1-be kell */
                ezen->c |= 1<<index;
            } else {    /* 0-ba kell */
                ezen->c &= ~(1<<index);
            }
            return *this;
        }

        /* Ez egy NAGYON furcsa operator=. Ugyanis ha azt írjuk,
         * hogy b[1] = b[2], akkor Proxy = Proxy értékadás történik.
         * Ilyenkor viszont nem a proxyt kell másolni,
         * hanem a hivatkozott biteket! */
        Proxy& operator=(const Proxy& masik) {
            *this = bool(masik);  /* this->operator=(masik.operator bool()); */
            return *this;
        }
    };

    Proxy operator[](int i) {
        return Proxy(this, i);
    }
};

int main() {
    BitTomb b;
    b[3] = 1;     /* operator=(bool uj); */
    std::cout << b[3] << std::endl; /* operator bool() */
    std::cout << b[5] << std::endl;
    b[5] = b[3];  /* Proxy::operator=(Proxy) */
    std::cout << b[5] << std::endl;
}

Így működik egyébként az std::vector<bool> specializáció is. Ezért nem lehet egy bool vektor egy elemének a címét sem képezni; akkor nem az adott bool címét képeznénk, hanem a segédobjektumét. Amúgy is, egy bitnek nem lehet memóriacíme: a legkisebb egység, aminek van, a bájt.