47832


(参考書:明解c++中級編)  
1添え字演算子[]の`return BitOfByteRef(vec[i / CHAR_BITS], (i & (CHAR_BITS - 1)));`  
の計算式がどのようになっているか解説してほしいです。

2また、添え字演算子[]の範囲エラーの`throw IdxRngErr(this, i);`でなぜthisも返す必要があるのかを知りたいです。//ここです

3また、真偽を設定という部分のコメントアウトしてる部分の式がないをしているかを教えていただけますでしょうか。

[code]

    BitOfByteRef& operator=(bool b) {                       // 真偽を設定
        if (b)
            vec |= 1 << idx;//
        else
            vec &= ~(1 << idx);//
    
        return *this;
    }
    
    
    
    template<> class Array<bool> {
        typedef unsigned char BYTE;
        static const int CHAR_BITS = std::numeric_limits<unsigned char>::digits;
    
        int nelem;      // bool型配列の要素数
        int velem;      // bool型配列を格納するためのBYTE型配列の要素数
        BYTE* vec;      // BYTE型先頭要素へのポインタ
    
        // bool型sz個の要素の格納に必要なBYTE型配列の要素数
        static int size_of(int sz) { return (sz + CHAR_BITS - 1) / CHAR_BITS; }
    
    public:
    
        void f() {
            std:: cout << CHAR_BIT << std::endl;
        }
    
    
        //=== ビットベクトル（バイト）中の１ビットへの参照を表すためのクラス ===//
        class BitOfByteRef {
            unsigned char& vec;     // 参照先BYTE
            int idx;        // 参照先BYTE中のビット番号
    
        public:
            BitOfByteRef(BYTE& r, int i) : vec(r), idx(i) { }       // コンストラクタ
    　　　　　　　　　　　　　　　　　　　　　　//ここです↓
            operator bool() const { return (vec >> idx) & 1; }      // 真偽を取得
    
            BitOfByteRef& operator=(bool b) {                       // 真偽を設定
                if (b)
                    vec |= 1 << idx;
                else
                    vec &= ~(1 << idx);
    
                return *this;
            }
        };
    
    
        //----- 添字範囲エラー -----//
        class IdxRngErr {
            const Array* ident;
            int index;
        public:
            IdxRngErr(const Array* p, int i) : ident(p), index(i) { }
            int Index() const { return index; }
        };
    
    
    
    
        //--- 明示的コンストラクタ ---//
        explicit Array(int sz, bool v = bool()) : nelem(sz), velem(size_of(sz)) {
            vec = new BYTE[velem];
            for (int i = 0; i < velem; i++)     // 全要素を初期化
                vec[i] = v;
        }
    
        //--- コピーコンストラクタ ---//
        Array(const Array& x) {
            if (&x == this) {                       // 初期化子が自分自身であれば…
                nelem = 0;
                vec = NULL;
            }
            else {
                delete[] vec;
    
                nelem = x.nelem;                    // 要素数をxと同じにする
                velem = x.velem;                    // 要素数をxと同じにする
                vec = new BYTE[velem];              // 配列本体を確保
                for (int i = 0; i < velem; i++)     // 全要素をコピー
                    vec[i] = x.vec[i];
            }
        }
    
        //--- デストラクタ ---//
        ~Array() { std::cout << "です\n"; delete[] vec; }
    
        //--- 要素数を返す ---//
        int size() const { return nelem; }
    
        //--- 代入演算子= ---//
        Array& operator=(const Array& x) {
            if (&x != this) {                   // 代入元が自分自身でなければ…
                if (velem != x.velem) {         // 代入前後の要素数が異なれば…
                    delete[] vec;               // もともと確保していた領域を解放
                    velem = x.velem;            // 新しい要素数
                    vec = new BYTE[velem];      // 新たに領域を確保
                }
    
                nelem = x.nelem;                // 新しい要素数
                for (int i = 0; i < velem; i++) // 全要素をコピー
                    vec[i] = x.vec[i];
            }
            return *this;
        }
    
        //--- 添字演算子[] ---//
        BitOfByteRef operator[](int i) {
            if (i < 0 || i >= nelem)
                throw IdxRngErr(this, i);               // 添字範囲エラー送出
    
            return BitOfByteRef(vec[i / CHAR_BITS], (i & (CHAR_BITS - 1)) );
        }
    
        //--- 添字演算子[] ---//
        bool operator[](int i) const {
            if (i < 0 || i >= nelem)
                throw IdxRngErr(this, i);   // 添字範囲エラー送出
    
            return (vec[i / CHAR_BITS] >> (i & (CHAR_BITS - 1)) & 1U) == 1;//
        }
    };
    
    #endif
    
[/code]


CC BY-SA 4.0
2018-08-24T08:31:26.603
47781
2018-08-27T21:19:29.273
2018-08-27T05:32:32.750
question
c++
c++,ビット演算の計算式


あなたが何がわかっていないのか読者的にわからないので、どこから説明すればよいのか微妙なところがありますが

まず前提として `bool` 値というのは `false` / `true` の２種類しかないので `bool` １つを 1bit
つまり２進数の１桁で表現できるということはわかりますか？提示ソースコードは `false` を２進数１桁の `0` で `true` を２進数１桁の `1`
で表記しようとしています。

このコードは大きな個数の `bool` をひとまとめに扱おうとしています。 `123` 個とか `34567` 個の `bool` を、それぞれに 1bit
を割り振って最小限の記憶域容量で扱おうとしています。 [c++](/questions/tagged/c%2b%2b "'c++'
のタグが付いた質問を表示") には１ビット単位でメモリを確保する機能は用意されていないので `new BYTE[]` で複数の `BYTE`
を確保することで代用しています。この際に、切り上げないと数が足らないのはわかりますよね。

話を簡単にするため `CHAR_BITS` が 8 ということにします。すると `char` というか `BYTE` １つに 8bit
が格納できるということになります。

A1.

  * `i/CHAR_BITS` は当該 `i` ビットが何バイト目か
  * `i & (CHAR_BITS - 1)` はそのバイト内での何ビット目か

ということになります。

[code]

    |x|x|x|x|x|x|x|x| (１バイトに８個の２進数が格納できる、その　BYTE の [0] 番目）
     | | | | | | | +--  [0] は０バイト目のビット０
     | | | | | | +----  [1] は０バイト目のビット１
     | | | | | +------  [2] は０バイト目のビット２
     +----------------  [7] は０バイト目のビット７
    |x|x|x|x|x|x|x|x|（同様 BYTE の [1] 番目）
     | | | | | | | +--  [8] は１バイト目のビット０
     | | | | | | +----  [9] は１バイト目のビット１
     +---------------- [15] は１バイト目のビット７
    
[/code]

`x&(8-1)` は `x%8`
と同値、つまり８で割ったときの剰余を求めています。いかにも２進数ぽいロジックなわけですが、これが理解できないと話は難しいです。 `x/8`
で求めた商は何バイト目か、剰余は何ビット目か、ですね。 `x%8`
とソースコード上書くとコンパイラが除算命令を生成するかもしれません（遅い）。この例ではビット演算命令で済む（高速）ので、提示ソースのように書いておけばコンパイラは除算命令を生成しないことが期待できます。

上記の話は `CHAR_BITS` が２の冪でないと成り立ちません。例えば `x%7` を `x&(7-1)`
とすることはできないわけです。でもまあ現代コンピュータでは `CHAR_BITS`
`は２の冪であることを期待して問題ないでしょう（そうでないものを見たことがない）。

A2. 例外を発生させると、どこで例外が発生したかはデバッガでわかります。が、その行に複数個の `Array<bool>`
が出現していると、どっちで例外が発生したかぱっと見わかりません。例外オブジェクトの中に `this` が入っていればデバッグが楽になります。

A3. どこのことでしょうか？コメントは入っていますが「コメントアウトされたコード」は見つけられませんでした。

`idx` は「何ビット目」か、式 `1<<idx` は「そのビットだけ１となった値」であることが理解できますか？ `idx==5` のとき
`(1<<idx)==0b00100000` ですよね。ならば `BYTE` 値の bit5 をセットするにはビット論理和つまり `|(1<<idx)`
でよいわけです。

同様 bit5 を取り出すにはビット論理積 `&` を使えばよいわけです（結果が `0` か、非 `0` かでよいなら。結果として `0` か `1`
がほしいなら話は別）

同様 bit5 をクリアするにはビット論理積 `&` と `0b00100000` のビット反転結果を使えばよくて、ビット反転演算子 `~`
をつければよいことになります。


CC BY-SA 4.0
2018-08-27T01:13:40.407
47832
2018-08-27T21:19:29.273
2018-08-27T21:19:29.273
8589
8589
47781
answer