先给出 dynamic_bitset 的一个简单示例, 以增加感性认识:
boost::dynamic_bitset<> a(2);
a[0] = true;
a[1] = false;
if (a[0])
std::cout << "a[0] holds true/n";
技术要点一: operator[ ] 返回了什么
返回了引用. 举例来说,
int a[2];
a[1] = 3;
if (a[1] == 4)
//...
则前者 a[1] = 3 实际上相当于
int& ra = *(a + 1);
ra = 3;
后者则相当于
if (ra == 4)
//...
所以如果我们要正确重载 operator[ ] , 就也应该返回相应数据的引用, 这一点可以参考std::vector 的实现.
技术要点二: proxy reference
在 dynamic_bitset 中,显然我们无法返回一个 "bit 引用", 怎么办? 我们做出一个与 "bit 引用"具有相同功能的数据类型. 首先,我们要看看 dynamic_bitset 的内部数据存储, 假设模板参数是unsigned int, 则下面是简化版:
class dynamic_bitset
{
std::vector<unsigned int> m_bits;
size_type m_num_bits;
// ...
};
以示例中的 a[1] = false 为例, 我们要引用的是 m_bits 中的第 1 个 unsigned int 的第 2 位, 因此我们的这个冒牌的 reference 看起来应该如下:
class bit_reference
{
unsigned int& m_block; // 引用第 1 个 unsigned int
int m_bit; // 引用第 1 个 unsigned int 的第 2 位
public:
bit_reference(unsigned int& block, int bit)
: m_block(block), m_bit(bit)
{}
};
因为是引用, 我们就应该有能力修改被引用的数据, 所以要紧紧抓住被引用的数据, 这就有了 m_block, 但是我们仅仅引用其中的一位(bit), 所以要记住这一位的位置.
一般的"reference" 最重要的用处应该有两点: 1.存数据, 2.取数据, 而那个"bit 引用",虽然不存在,但也要求具有这两项功能: 1.存一个bool, 2.取一个 bool. 所以这个 bit_reference 要实现这两点:
class bit_reference
{
// ...
// 存 bool
bit_reference& operator=(bool x) {
if (x)
m_block |= (1 << m_bit);
else
m_block &= ~(1 << m_bit);
return *this;
}
// 取 bool
operator bool() const {
return (m_block & (1 << m_bit) ) != 0;
}
有了这个"位引用", 我们就可以在 dynamic_bitset 中用上它了:
class dynamic_bitset
{
// ...
public:
bit_reference operator[ ](size_type pos) {
int nBlock = pos / sizeof(unsigned int);
int nBit = pos % sizeof(unsigned int);
return bit_reference(m_bits[nBlock], nBit);
}
到现在, 这个冒牌的reference基本上达到了我们的要求, 而且这个 dynamic_bitset 也基本上可以跑起来了,下面我们看看运行的过程, 以最前面的示例为例:
1. a[0] = true
调用过程为:
bit_reference dynamic_bitset::operator[ ](0);
bit_reference& bit_reference::operator=(bool x);
2. if (a[1])
调用过程为:
bit_reference dynamic_bitset::operator[ ](1);
bit_reference::operator bool ();
这个 bit_reference 就是我们所说的 proxy reference. 当我们无法实现原有数据类型的引用时, 我们就用另外一种数据类型来模仿这种引用, 而这种数据类型起码要实现两点: 存,取. 从广义上来说, STL 里面的 iterator 大多都是 proxy reference (除vector::iterator), 只是它们实现的功能在数据结构中都是显而易见的, 大家司空见惯了.
proxy reference 还有另外一个用处, 就是写时复制, 我们可以看到: 通过 proxy reference 我们只能从一个地方修改被引用的数据(以bit_reference 为例): bit_reference& operator=(bool x). 在这个函数里,我们就可以很容易地实现写时复制(copy on write). 在 <<The C++ Programming Language>>里有一个 string 类的写时复制实现, 使用的就是这种技术, 我们可以参考.
boost::dynamic_bitset<> a(2);
a[0] = true;
a[1] = false;
if (a[0])
std::cout << "a[0] holds true/n";
技术要点一: operator[ ] 返回了什么
返回了引用. 举例来说,
int a[2];
a[1] = 3;
if (a[1] == 4)
//...
则前者 a[1] = 3 实际上相当于
int& ra = *(a + 1);
ra = 3;
后者则相当于
if (ra == 4)
//...
所以如果我们要正确重载 operator[ ] , 就也应该返回相应数据的引用, 这一点可以参考std::vector 的实现.
技术要点二: proxy reference
在 dynamic_bitset 中,显然我们无法返回一个 "bit 引用", 怎么办? 我们做出一个与 "bit 引用"具有相同功能的数据类型. 首先,我们要看看 dynamic_bitset 的内部数据存储, 假设模板参数是unsigned int, 则下面是简化版:
class dynamic_bitset
{
std::vector<unsigned int> m_bits;
size_type m_num_bits;
// ...
};
以示例中的 a[1] = false 为例, 我们要引用的是 m_bits 中的第 1 个 unsigned int 的第 2 位, 因此我们的这个冒牌的 reference 看起来应该如下:
class bit_reference
{
unsigned int& m_block; // 引用第 1 个 unsigned int
int m_bit; // 引用第 1 个 unsigned int 的第 2 位
public:
bit_reference(unsigned int& block, int bit)
: m_block(block), m_bit(bit)
{}
};
因为是引用, 我们就应该有能力修改被引用的数据, 所以要紧紧抓住被引用的数据, 这就有了 m_block, 但是我们仅仅引用其中的一位(bit), 所以要记住这一位的位置.
一般的"reference" 最重要的用处应该有两点: 1.存数据, 2.取数据, 而那个"bit 引用",虽然不存在,但也要求具有这两项功能: 1.存一个bool, 2.取一个 bool. 所以这个 bit_reference 要实现这两点:
class bit_reference
{
// ...
// 存 bool
bit_reference& operator=(bool x) {
if (x)
m_block |= (1 << m_bit);
else
m_block &= ~(1 << m_bit);
return *this;
}
// 取 bool
operator bool() const {
return (m_block & (1 << m_bit) ) != 0;
}
有了这个"位引用", 我们就可以在 dynamic_bitset 中用上它了:
class dynamic_bitset
{
// ...
public:
bit_reference operator[ ](size_type pos) {
int nBlock = pos / sizeof(unsigned int);
int nBit = pos % sizeof(unsigned int);
return bit_reference(m_bits[nBlock], nBit);
}
到现在, 这个冒牌的reference基本上达到了我们的要求, 而且这个 dynamic_bitset 也基本上可以跑起来了,下面我们看看运行的过程, 以最前面的示例为例:
1. a[0] = true
调用过程为:
bit_reference dynamic_bitset::operator[ ](0);
bit_reference& bit_reference::operator=(bool x);
2. if (a[1])
调用过程为:
bit_reference dynamic_bitset::operator[ ](1);
bit_reference::operator bool ();
这个 bit_reference 就是我们所说的 proxy reference. 当我们无法实现原有数据类型的引用时, 我们就用另外一种数据类型来模仿这种引用, 而这种数据类型起码要实现两点: 存,取. 从广义上来说, STL 里面的 iterator 大多都是 proxy reference (除vector::iterator), 只是它们实现的功能在数据结构中都是显而易见的, 大家司空见惯了.
proxy reference 还有另外一个用处, 就是写时复制, 我们可以看到: 通过 proxy reference 我们只能从一个地方修改被引用的数据(以bit_reference 为例): bit_reference& operator=(bool x). 在这个函数里,我们就可以很容易地实现写时复制(copy on write). 在 <<The C++ Programming Language>>里有一个 string 类的写时复制实现, 使用的就是这种技术, 我们可以参考.