#include <iostream>
#include <vector>
#include <string>
class Ringo {
public:
enum color_index { color_red, color_green, color_yellow };
列挙型の各定数の型は、列挙型(enum)の型です。具体的には、enum color_index という列挙型が定義されています。この列挙型は、3つの定数を持っています:color_red、color_green、color_yellow。それぞれの定数は、color_index 列挙型のインスタンスです。
C++ の列挙型では、デフォルトでは定数の型は int です。したがって、color_red、color_green、color_yellow は実際には int 型の定数です。ただし、個々の列挙型定数は列挙型自体のスコープに紐付けられており、Ringo:: を付けて特定の列挙型定数を参照することができます。
color_index color; // Color
double weight; // Weight
std::string kind; // Variety
Ringo() : color(color_red), weight(320.0), kind("Fuji") {}
Ringo(const enum color_index& c, const double w, const char* k) : color(c), weight(w), kind(k) {}
color_index 列挙型と color, weight, kind メンバ変数は、Ringo クラスのリンゴのオブジェクトを表すために使用されます。クラスのコンストラクタは、これらのメンバ変数を初期化するために使われています。
デフォルトコンストラクタは引数がないときに、引数付きコンストラクタは新しいリンゴを特定の属性で生成したいときに使われます。
const char* GetColorName() const {
static const char* color_name[] = { "Red", "Green", "Yellow" };
return color_name[color];
}
クラスの静的メンバ static(変数や関数)は、そのクラスの各オブジェクト間で共有されます。
静的メンバは、クラスのインスタンスが存在しなくてもアクセスすることができます。静的変数は、そのクラスの全てのオブジェクトに共通のデータを保持するために使用されることが多いです。
void PrintData() const {
printf("Color=%s Weight=%.1fg Variety=%s\n", GetColorName(), weight, kind.c_str());
}
};
.c_str() は C++ 標準ライブラリにある std::string クラスのメンバ関数です。
この関数は std::string オブジェクトを表す文字列を C スタイルの文字列、つまり const char* 型へと変換します。このポインタは文字列の最初の文字を指し、ヌル終端されているため、C スタイルの文字列として扱うことができます。
class RingoBox {
public:
RingoBox() {}
bool Add(Ringo r);
bool Del(int index);
void Empty() { ringo.clear(); }
int GetTotalNum() const { return static_cast<int>(ringo.size()); }
double GetTotalWeight() const;
void PrintData() const;
関数の戻り値の型として bool が指定されている場合、その関数は真偽値を返すことが期待されます。
このコンテキストで bool Add(Ringo r); は、Ringo オブジェクト r を何らかのコレクションに追加しようとする関数です。追加操作が成功した場合は true を、何らかの理由で追加ができなかった場合は false を返します。
clear() は std::vector のメンバ関数で、ベクター(動的配列)から全ての要素を削除し、その容量を0にします。
この操作はベクターに含まれる全ての要素のデストラクタを呼び出しますが、ベクターの確保しているメモリ自体の容量は変更しません(メモリは解放されずに残ります)。つまり、clear() を呼び出した後も、std::vector は以前に確保したメモリ容量(capacity() で取得可能)を保持しますが、要素数(size() で取得可能)は0になります。
private:
std::vector<Ringo> ringo;
};
bool RingoBox::Add(Ringo r) {
if (ringo.size() >= 10) return false; // If capacity is over, return false
ringo.push_back(r);
return true;
}
bool RingoBox::Del(int index) {
if (index < 1 || index > static_cast<int>(ringo.size())) return false; // Return false if index is out of range
ringo.erase(ringo.begin() + (index - 1));
return true;
}
これは、std::vector の erase メソッドを使って、ringo ベクター内の特定の位置にある要素を削除しています。
ringo.begin():ringo ベクターの先頭を指すイテレータです。
index - 1:削除する要素のインデックスです。C++ のインデックスは 0 から始まるため、ユーザーが指定するインデックスから 1 を引く必要があります。
ringo.begin() + (index - 1):削除する要素の位置を指すイテレータです。指定されたインデックスが 1 であれば、これはベクターの 0 番目の要素を指します。
ringo.erase(ringo.begin() + (index - 1)):指定された位置にある要素を削除します。
例えば、index が 3 の場合、指定された位置にある 3 番目の要素が削除されます。ベクターのインデックスは 0 から始まるので、実際に削除されるのはベクターの 2 番目の要素になります。
double RingoBox::GetTotalWeight() const {
double w = 0.0;
for (const auto& apple : ringo) {
w += apple.weight;
}
return w;
}
const auto& は C++ の範囲ベースのforループにおいて使用される宣言であり、コンテナ内の各要素に対する参照を得るために使用されます。
auto キーワードは、変数の型を自動的に推論してくれる機能です。この場合、auto はループが進むコンテナ ringo(std::vector 型)の要素の型を自動的に推論し、それを変数 apple の型として使用します。ringo の要素は Ringo 型なので、apple は Ringo 型になります。
アンパサンド (&) は参照を示します。この場合、apple が Ringo オブジェクトそのものではなく、そのオブジェクトへの参照を保持することを意味します。参照を使用することで、コンテナ内の実際のオブジェクトを直接操作することができ(ここでは変更は行いませんが)、またオブジェクトのコピーを作成することなく効率的にループを行うことができます。
const 修飾子は、この参照を通じてオブジェクトを変更できないことを意味します。つまり、apple を通じて Ringo オブジェクトの内容を変更することはできません。これは、オブジェクトのデータを保護し、誤って変更されることがないようにするために重要です。
void RingoBox::PrintData() const {
int n = GetTotalNum();
for (int i = 0; i < n; ++i) {
ringo[i].PrintData(); // 各リンゴのPrintDataメソッドを呼び出す
}
printf("Total weight=%.1fg\n", GetTotalWeight());
}
RingoBox クラスの PrintData() メソッド内で、ringo ベクターに格納されている各 Ringo オブジェクトの PrintData() メソッドを呼び出すことにより、各リンゴのデータ(色、重さ、品種)を出力しています。
int main() {
const char init_num = 4;
RingoBox myRingoBox;
Ringo init_ringo[] = {
Ringo(Ringo::color_red, 316.2, "Tsugaru"),
Ringo(Ringo::color_green, 351.4, "Granny Smith"),
Ringo(Ringo::color_red, 348.1, "Jonagold"),
Ringo(Ringo::color_yellow, 320.7, "Golden Delicious")
};
// Add apples
for (int i = 0; i < init_num; ++i) {
bool ret = myRingoBox.Add(init_ringo[i]);
// Display the result of addition
std::cout << i + 1 << "th apple added " << (ret ? "successfully" : "failed") << std::endl;
}
// Delete the second one
bool ret = myRingoBox.Del(2);
if (ret) {
std::cout << "2nd apple deleted" << std::endl;
}
// Display all data
myRingoBox.PrintData();
return 0;
}