Di banyak program muncul kebutuhan yang terlihat sederhana: menyimpan puluhan jawaban “ya/tidak” dalam satu wadah ringkas — fitur aktif atau tidak, izin diberikan atau dicabut, sel sudah dilewati atau belum. Kita bisa menyusun std::vector<bool>, tetapi C++ punya wadah yang dirancang khusus untuk kebutuhan ini: std::bitset<N> (header <bitset>), deret tepat N bit yang ukurannya ditetapkan saat kompilasi. Setiap bit hanya bernilai 0 atau 1, dan bitset menyediakan operasi yang justru paling alami untuk bit: menyalakan, mematikan, membalik, menghitung, serta operasi bitwise antar dua bitset. Chapter ini berjalan dari operasi per-bit dasar, operasi bitwise antar bitset, hingga use case flag yang paling sering kita temui di kode nyata.
Deret Bit Berukuran Tetap #
std::bitset<8> membuat delapan bit yang semuanya bernilai 0 saat dibuat; std::bitset<32> membuat tiga puluh dua. Ukuran N adalah bagian dari tipe, jadi dua bitset dengan ukuran berbeda adalah tipe yang berbeda dan tidak bisa dicampur begitu saja. Konsekuensinya, bitset tidak bisa bertambah menyusut seperti vector — kebutuhan seperti itu justru tanda bahwa kita perlu std::vector<bool>, bukan bitset.
Cara membaca tampilannya penting dipahami sejak awal: ketika bitset dicetak, bit tertinggi (indeks N-1) berada di kiri dan bit indeks 0 berada di kanan. Jadi 00001001 berarti bit 0 dan bit 3 yang menyala — persis seperti menulis bilangan biner biasa. Metode to_string() menghasilkan deret karakter yang urutannya sama, dan operator<< mencetaknya tanpa perlu to_string().
Set, Reset, Test, Flip #
Program berikut mengoperasikan satu bitset<8> dari nol hingga konversi ke bilangan bulat. Jalankan dan perhatikan bagaimana posisi bit sesuai dengan urutan pencetakan yang baru kita bahas.
#include <bitset> // std::bitset
#include <iostream> // std::cout
#include <string> // std::string
int main() {
// bitset<8>: delapan bit berukuran tetap, semua bernilai 0 saat dibuat
std::bitset<8> status;
std::cout << "awal : " << status.to_string() << "\n";
// set(pos): nyalakan satu bit; set() tanpa argumen menyalakan semua
status.set(0);
status.set(3);
std::cout << "set(0), set(3): " << status.to_string() << "\n";
// test(pos): baca satu bit tanpa mengubahnya
std::cout << "test(3) : " << status.test(3) << "\n";
std::cout << "test(5) : " << status.test(5) << "\n";
// flip(pos): balikkan satu bit; flip() tanpa argumen membalik semua
status.flip(3);
std::cout << "flip(3) : " << status.to_string() << "\n";
// operator[] juga bisa baca dan tulis
status[6] = 1;
std::cout << "status[6] = 1 : " << status.to_string() << "\n";
// count: banyak bit menyala; any/none/all melaporkan kondisi keseluruhan
std::cout << "count : " << status.count() << "\n";
std::cout << "any/none/all : " << status.any() << " " << status.none()
<< " " << status.all() << "\n";
// reset(pos): matikan satu bit; reset() tanpa argumen mematikan semua
status.reset(0);
std::cout << "reset(0) : " << status.to_string() << "\n";
// konversi dua arah: dari string dan ke bilangan bulat
std::bitset<8> dariString(std::string("11000101"));
std::cout << "dari string : " << dariString.to_string()
<< " = " << dariString.to_ulong() << "\n";
return 0;
}Output yang Dihasilkan:
awal : 00000000
set(0), set(3): 00001001
test(3) : 1
test(5) : 0
flip(3) : 00000001
status[6] = 1 : 01000001
count : 2
any/none/all : 1 0 0
reset(0) : 01000000
dari string : 11000101 = 197Bedah baris demi baris:
- Baris 7 (
std::bitset<8> status;): delapan bit, semuanya 0. Tidak ada argumen konstruktor yang wajib — keadaan awalnya sudah jelas. - Baris 11-12 (
status.set(0); status.set(3);): menyalakan bit 0 dan bit 3, tercetak00001001— kiri adalah bit 7, kanan adalah bit 0. - Baris 16-17 (
status.test(3)): membaca satu bit tanpa mengubahnya: bit 3 menyala (1), bit 5 mati (0).status[3]setara untuk baca, tetapitest()juga memeriksa batas dan melempar exception bila indeks di luarN. - Baris 20 (
status.flip(3)): membalik bit 3 dari 1 menjadi 0; hasilnya00000001. - Baris 24 (
status[6] = 1;): operator[] bisa menulis juga — bit 6 menyala, tercetak01000001. - Baris 28-29 (
count,any/none/all):count()menghitung bit yang menyala (kini 2),any()bertanya “ada bit menyala?”,none()“semua mati?”,all()“semua menyala?”. - Baris 32 (
status.reset(0)): mematikan bit 0, menyisakan01000000. - Baris 36-37 (konversi): bitset bisa dibangun dari
std::stringberisi0/1dan dikonversi keluar:to_ulong()mengembalikan nilainya sebagai bilangan bulat —11000101bernilai197.
Operasi Bitwise Antar bitset #
Dua bitset berukuran sama dapat dioperasikan langsung: & (AND), | (OR), ^ (XOR), dan ~ (NOT) bekerja per posisi bit, sementara << dan >> menggeser seluruh deret. Karena operasi ini milik bahasa sejak C dari awal, intuisinya langsung terbawa ke bitset.
#include <bitset> // std::bitset
#include <iostream> // std::cout
#include <string> // std::string
int main() {
// operasi bitwise antar dua bitset: &, |, ^, ~
std::bitset<8> a(std::string("11001100"));
std::bitset<8> b(std::string("10101010"));
std::cout << "a : " << a << "\n";
std::cout << "b : " << b << "\n";
std::cout << "a & b : " << (a & b) << "\n";
std::cout << "a | b : " << (a | b) << "\n";
std::cout << "a ^ b : " << (a ^ b) << "\n";
std::cout << "~a : " << (~a) << "\n";
// geser bit: kiri menggandakan, kanan membagi dua (untuk bilangan)
std::bitset<8> c(std::string("00001111"));
std::cout << "c << 2 : " << (c << 2) << "\n";
std::cout << "c >> 2 : " << (c >> 2) << "\n";
// use case: kumpulan flag izin dalam satu bitset
enum Posisi { Baca = 0, Tulis, Hapus };
std::bitset<3> izin;
izin[Baca] = true;
izin[Tulis] = true;
std::cout << "izin awal: " << izin << "\n";
izin[Hapus] = true; // diberi izin hapus
izin[Tulis] = false; // dicabut izin tulis
std::cout << "izin kini: " << izin << " (Baca? " << izin[Baca]
<< ", Tulis? " << izin[Tulis] << ")\n";
return 0;
}Output yang Dihasilkan:
a : 11001100
b : 10101010
a & b : 10001000
a | b : 11101110
a ^ b : 01100110
~a : 00110011
c << 2 : 00111100
c >> 2 : 00000011
izin awal: 011
izin kini: 101 (Baca? 1, Tulis? 0)Poin-poin yang perlu kita perhatikan:
- Baris 12-14 (
&,|,^): AND menyala hanya bila kedua bit menyala, OR bila salah satu menyala, XOR bila keduanya berbeda. Cocokkan baris demi baris denganadanbdi atasnya. - Baris 15 (
~a): NOT membalik semua bit — kebalikan sempurna daria. - Baris 19-20 (geser):
c << 2menggeser deret ke arah bit tinggi,c >> 2ke arah bit rendah; pada nilai biner itu setara menggandakan dan membagi dua per langkah geser. - Baris 23-29 (flag izin): enum memberi nama untuk posisi bit —
Bacabit 0,Tulisbit 1,Hapusbit 2. Memberi dan mencabut izin adalah sekadar menyalakan/mematikan satu bit, dan satucount()langsung melaporkan berapa izin aktif.
Kapan bitset Tepat Dipakai #
Bitset bersinar ketika jumlah flag diketahui sejak penulisan kode dan operasinya berbasis bit: status sel pada peta, tanda sudah-dikunjungi pada graf, kumpulan izin, atau filter sederhana. Operasi bitwise antar bitset membuat pembandingan sekumpulan flag jadi satu ekspresi — a & b menghasilkan flag yang menyala di keduanya tanpa loop manual. Untuk kebutuhan serupa dengan ukuran yang baru diketahui saat runtime, pakailah std::vector<bool>; untuk kebutuhan matematika bit yang lebih dalam, header <bit> menyediakan alat lanjutan di luar cakupan chapter ini.
Satu batas terakhir perlu diingat: bitset bukan container STL sejati — ia tidak punya iterator dan tidak bisa diiterasi dengan range-based for. Ia adalah tipe utilitas deret bit, dan perannya melengkapi container yang sudah kita kuasai. Chapter berikutnya beralih ke header <numeric>, yang menyimpan fungsi perhitungan kecil seperti gcd, lcm, dan midpoint — alat-alat pendek yang sering menyelamatkan kode sehari-hari.
Rujukan resmi: cppreference — std::bitset , cppreference — header <bitset> .