数字の配列を含む Number クラスの場合を考えてみましょう。クラスのユーザーが数字に簡単にアクセスできるようにしたいので、[] 演算子をオーバーロードして、ユーザーが 10 の累乗で数字を選択できるようにします。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 としてマークされていない限り呼び出すことはできません。ただし、数値 & が必要であり、const Number & が渡されるため、Digit クラスをインスタンス化することはできません。
1 つの解決策には const_cast の使用が含まれますが、ハックを使用しているように感じます。言うまでもなく、const を削除すると、ユーザーは Digit の演算子 = を使用して数値インスタンスを変更できるようになります。const_cast を使用したり、ConstDigit などの別のクラスを作成したりする以外に、この問題を別の方法で解決する方法はありますか?
もう 1 つの問題: Digit のインスタンスは、ユーザーの快適さのためにのみ提供されているため、ユーザーに保存してほしくありません。プライベート コピー コンストラクターは、ユーザーが次のような数字を格納できないようにします。
auto digit = number[5];
ただし、参照によって数字を保存することはできます。
auto digit & = number[5];
数値は参照によって数字に渡されますが (Digit コンストラクターを参照)、ポインターの問題を防ぎますが、この状況では AV を防ぐことはできません。
auto number = new Number();
auto & digit = (*number)[5];
delete number;
digit = 12;
ユーザーが Number で new 演算子を使用することを禁止するかもしれませんが、別の方法があるのでしょうか?
より長い答え、参照によって数字を返せない理由。
数値は 2 つの個別のベクトルとして保持されます。1 つは整数部分用で、もう 1 つは小数部分用です (問題の例を単純化しましたが、ここではあまり重要ではありません)。例えば、
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 桁を追加する必要はなく、確認する方法さえありません。先行ゼロと後続ゼロが存在します)。彼の割り当て後にストレージを圧縮する方法もありません。
もちろん、解決策の 1 つは、[] 演算子を削除して getter/setter を使用することです。しかし、全体のポイントは、クラスを使いやすくすることであり、[] 演算子は、getter/setter メソッドのペアよりもはるかにこの仮定を満たします:)