考虑包含数字数组的 Number 类的情况。我希望类用户轻松访问数字,因此我重载了 [] 运算符,以便用户可以选择十的幂,例如。number[2] 是代表百的数字,以此类推。该类是索引安全的 - 如果用户指定的索引超出范围,则返回 0(如 10 = 0010)。
但是,设置一个数字需要更多的努力,因为如果指数超过了保存数字的内部表的大小,则必须扩展后者。
很明显,必须选择不同的方法,用户是否要检索数字以及何时要存储它。我想出了以下解决方案(这些类是部分的,只是为了展示这个想法):
class Number;
class Digit
{
friend class Number;
private:
Number number;
int exponent;
Digit(Number & newNumber, int newExponent)
: number(newNumber), exponent(newExponent)
{
}
public:
operator unsigned char()
{
return number.GetDigit(exponent);
}
void operator = (unsigned char digit)
{
number.SetDigit(exponent, digit);
}
};
class Number
{
friend class Digit;
private:
unsigned char GetDigit(int exponent) { ... };
void SetDigit(int exponent, unsigned char value) { ... };
public:
Digit operator [] (int exponent)
{
return Digit(*this, exponent);
}
}
Digit 类中的运算符显然调用了一些私有 Number 的方法,这样用户就可以分配一个值并检索一个值。我也可以采取不同的行动,无论用户是否想要检索价值以及何时想要存储它。
然而,const 有一个问题。如果 Number 类实例是 const,则不能调用运算符 [],除非它也被标记为 const。但是,无法实例化 Digit 类,因为它需要 Number &,而传递 const Number &。
一种解决方案涉及使用 const_cast,但感觉就像使用 hack - 更不用说,删除 const 将允许用户使用 Digit 的运算符 = 修改数字实例。有没有办法解决这个问题,而不是使用 const_cast 或编写另一个类,比如 ConstDigit?
另一个问题:我不希望用户存储 Digit 的实例,因为它只是为了用户的舒适而提供的。私有复制构造函数阻止用户存储数字,例如:
auto digit = number[5];
但是,他仍然可以通过引用存储数字:
auto digit & = number[5];
该数字通过引用传递给 digit(请参阅 Digit 构造函数)以防止指针出现问题,但在这种情况下它不会阻止 AV:
auto number = new Number();
auto & digit = (*number)[5];
delete number;
digit = 12;
我可能会禁止用户在 Number 上使用 new 运算符,但也许还有另一种方法?
更长的答案,为什么我不能通过引用返回数字。
该数字被保存为两个独立的向量,一个用于整数部分,一个用于小数部分(我已经简化了相关示例,但在这里并不重要)。例如,
123.456
存储为
intPart : {3, 2, 1}
fracPart : {4, 5, 6}
如果用户请求第 100 位的数字,我只需检查它是否在数组边界之外并返回 0(因为 - 正如我所提到的,10 = 0010 = 00010 = ... 等等)
但是,如果用户希望设置数字,我必须扩展数组以填充所有剩余的指数,例如。
100 // user wants to set millions' digit to 5
5 [000] 100 // i have to add these empty digits
当然,我也可以在 [] 运算符中这样做。但是,如果用户决定将第 100 位设置为 0,我将不得不添加 98 位,如果他尝试这样做,我什至无法检查(并且该数字应该以最小可能的表示形式存储,两者都不前导零和尾随零存在)。我也没有办法在他分配后压缩存储空间。
当然,其中一种解决方案是删除 [] 运算符并使用 getter/setter。但重点是使类易于使用,并且 [] 运算符比一对 getter/setter 方法更能满足这个假设:)