第2章:課題の実装
はじめに
本章では、CPP Module 00の3つの演習を実装します。各演習の要件分析、設計アプローチ、そして実装のポイントを解説します。
---
1. ex00: Megaphone
1.1 課題の要件
- コマンドライン引数を大文字に変換して出力する
- 引数がない場合、デフォルトメッセージを出力する
- 複数の引数はスペースなしで連結する
期待される動作:
___CODE_BLOCK_0___gt; ./megaphone "shhhhh... I think the students are asleep..."
SHHHHH... I THINK THE STUDENTS ARE ASLEEP...
___CODE_BLOCK_0___gt; ./megaphone Damnit " ! " "Sorry students, I thought this thing was off."
DAMNIT ! SORRY STUDENTS, I THOUGHT THIS THING WAS OFF.
___CODE_BLOCK_0___gt; ./megaphone
* LOUD AND UNBEARABLE FEEDBACK NOISE *
1.2 設計アプローチ
[main(argc, argv)]
│
├── argc == 1 → デフォルトメッセージ出力
│
└── argc > 1 → 各引数を処理
│
└── 各文字を大文字変換して出力
1.3 実装
// megaphone.cpp
#include <iostream>
#include <cctype>
int main(int argc, char **argv) {
if (argc == 1) {
std::cout << "* LOUD AND UNBEARABLE FEEDBACK NOISE *" << std::endl;
return 0;
}
for (int i = 1; i < argc; i++) {
for (int j = 0; argv[i][j]; j++) {
std::cout << static_cast<char>(std::toupper(argv[i][j]));
}
}
std::cout << std::endl;
return 0;
}
1.4 ポイント解説
std::toupper の使用:
#include <cctype>
char upper = std::toupper('a'); // 'A'
// 注意: std::toupperはintを返すので、charにキャストする
std::cout << static_cast<char>(std::toupper(c));
static_cast: C++では型変換にキャスト演算子を使用します:
// C言語スタイル(非推奨)
char c = (char)std::toupper('a');
// C++スタイル(推奨)
char c = static_cast<char>(std::toupper('a'));
---
2. ex01: PhoneBook
2.1 課題の要件
- 電話帳アプリケーションを作成する
- 最大8件の連絡先を管理する
- ADD、SEARCH、EXITコマンドを実装する
- 古い連絡先は新しいもので上書きされる
2.2 Contactクラスの設計
// Contact.hpp
#ifndef CONTACT_HPP
#define CONTACT_HPP
#include <string>
class Contact {
private:
std::string firstName;
std::string lastName;
std::string nickname;
std::string phoneNumber;
std::string darkestSecret;
public:
// Orthodox Canonical Form
Contact();
Contact(const Contact& other);
Contact& operator=(const Contact& other);
~Contact();
// ゲッター
std::string getFirstName() const;
std::string getLastName() const;
std::string getNickname() const;
std::string getPhoneNumber() const;
std::string getDarkestSecret() const;
// セッター
void setFirstName(const std::string& value);
void setLastName(const std::string& value);
void setNickname(const std::string& value);
void setPhoneNumber(const std::string& value);
void setDarkestSecret(const std::string& value);
// 表示
void displayShort(int index) const;
void displayFull() const;
// 検証
bool isEmpty() const;
};
#endif
2.3 Contactクラスの実装
// Contact.cpp
#include "Contact.hpp"
#include <iostream>
#include <iomanip>
// コンストラクタ
Contact::Contact() {}
Contact::Contact(const Contact& other) {
*this = other;
}
Contact& Contact::operator=(const Contact& other) {
if (this != &other) {
firstName = other.firstName;
lastName = other.lastName;
nickname = other.nickname;
phoneNumber = other.phoneNumber;
darkestSecret = other.darkestSecret;
}
return *this;
}
Contact::~Contact() {}
// ゲッター
std::string Contact::getFirstName() const { return firstName; }
std::string Contact::getLastName() const { return lastName; }
std::string Contact::getNickname() const { return nickname; }
std::string Contact::getPhoneNumber() const { return phoneNumber; }
std::string Contact::getDarkestSecret() const { return darkestSecret; }
// セッター
void Contact::setFirstName(const std::string& value) { firstName = value; }
void Contact::setLastName(const std::string& value) { lastName = value; }
void Contact::setNickname(const std::string& value) { nickname = value; }
void Contact::setPhoneNumber(const std::string& value) { phoneNumber = value; }
void Contact::setDarkestSecret(const std::string& value) { darkestSecret = value; }
// 10文字に切り詰めるヘルパー関数
static std::string truncate(const std::string& str) {
if (str.length() > 10) {
return str.substr(0, 9) + ".";
}
return str;
}
// 短縮表示(一覧用)
void Contact::displayShort(int index) const {
std::cout << std::setw(10) << std::right << index << "|";
std::cout << std::setw(10) << std::right << truncate(firstName) << "|";
std::cout << std::setw(10) << std::right << truncate(lastName) << "|";
std::cout << std::setw(10) << std::right << truncate(nickname) << std::endl;
}
// 詳細表示
void Contact::displayFull() const {
std::cout << "First Name: " << firstName << std::endl;
std::cout << "Last Name: " << lastName << std::endl;
std::cout << "Nickname: " << nickname << std::endl;
std::cout << "Phone Number: " << phoneNumber << std::endl;
std::cout << "Darkest Secret: " << darkestSecret << std::endl;
}
bool Contact::isEmpty() const {
return firstName.empty();
}
2.4 PhoneBookクラスの設計
// PhoneBook.hpp
#ifndef PHONEBOOK_HPP
#define PHONEBOOK_HPP
#include "Contact.hpp"
class PhoneBook {
private:
Contact contacts[8];
int count;
int oldestIndex;
std::string getInput(const std::string& prompt) const;
void displayHeader() const;
public:
PhoneBook();
PhoneBook(const PhoneBook& other);
PhoneBook& operator=(const PhoneBook& other);
~PhoneBook();
void add();
void search() const;
};
#endif
2.5 PhoneBookクラスの実装
// PhoneBook.cpp
#include "PhoneBook.hpp"
#include <iostream>
#include <iomanip>
#include <limits>
#include <cstdlib>
PhoneBook::PhoneBook() : count(0), oldestIndex(0) {}
PhoneBook::PhoneBook(const PhoneBook& other) {
*this = other;
}
PhoneBook& PhoneBook::operator=(const PhoneBook& other) {
if (this != &other) {
for (int i = 0; i < 8; i++) {
contacts[i] = other.contacts[i];
}
count = other.count;
oldestIndex = other.oldestIndex;
}
return *this;
}
PhoneBook::~PhoneBook() {}
std::string PhoneBook::getInput(const std::string& prompt) const {
std::string input;
while (true) {
std::cout << prompt;
if (!std::getline(std::cin, input)) {
// EOFの処理
std::cout << std::endl;
std::exit(0);
}
if (!input.empty()) {
break;
}
std::cout << "Input cannot be empty." << std::endl;
}
return input;
}
void PhoneBook::add() {
Contact newContact;
newContact.setFirstName(getInput("First Name: "));
newContact.setLastName(getInput("Last Name: "));
newContact.setNickname(getInput("Nickname: "));
newContact.setPhoneNumber(getInput("Phone Number: "));
newContact.setDarkestSecret(getInput("Darkest Secret: "));
if (count < 8) {
contacts[count] = newContact;
count++;
} else {
contacts[oldestIndex] = newContact;
oldestIndex = (oldestIndex + 1) % 8;
}
std::cout << "Contact added successfully!" << std::endl;
}
void PhoneBook::displayHeader() const {
std::cout << std::setw(10) << std::right << "Index" << "|";
std::cout << std::setw(10) << std::right << "First Name" << "|";
std::cout << std::setw(10) << std::right << "Last Name" << "|";
std::cout << std::setw(10) << std::right << "Nickname" << std::endl;
std::cout << std::string(44, '-') << std::endl;
}
void PhoneBook::search() const {
if (count == 0) {
std::cout << "PhoneBook is empty." << std::endl;
return;
}
displayHeader();
for (int i = 0; i < count; i++) {
contacts[i].displayShort(i);
}
std::cout << "Enter index to view details: ";
std::string input;
if (!std::getline(std::cin, input)) {
std::cout << std::endl;
return;
}
// 入力の検証
if (input.empty() || input.find_first_not_of("0123456789") != std::string::npos) {
std::cout << "Invalid index." << std::endl;
return;
}
int index = std::atoi(input.c_str());
if (index < 0 || index >= count) {
std::cout << "Index out of range." << std::endl;
return;
}
contacts[index].displayFull();
}
2.6 メインプログラム
// main.cpp
#include "PhoneBook.hpp"
#include <iostream>
int main() {
PhoneBook phoneBook;
std::string command;
while (true) {
std::cout << "Enter command (ADD, SEARCH, EXIT): ";
if (!std::getline(std::cin, command)) {
std::cout << std::endl;
break;
}
if (command == "ADD") {
phoneBook.add();
} else if (command == "SEARCH") {
phoneBook.search();
} else if (command == "EXIT") {
break;
} else {
std::cout << "Unknown command. Use ADD, SEARCH, or EXIT." << std::endl;
}
}
return 0;
}
---
3. ex02: Account
3.1 課題の要件
3.2 ヘッダファイルの分析
// Account.hpp(提供される)
class Account {
public:
typedef Account t;
static int getNbAccounts(void);
static int getTotalAmount(void);
static int getNbDeposits(void);
static int getNbWithdrawals(void);
static void displayAccountsInfos(void);
Account(int initial_deposit);
~Account(void);
void makeDeposit(int deposit);
bool makeWithdrawal(int withdrawal);
int checkAmount(void) const;
void displayStatus(void) const;
private:
static int _nbAccounts;
static int _totalAmount;
static int _totalNbDeposits;
static int _totalNbWithdrawals;
static void _displayTimestamp(void);
int _accountIndex;
int _amount;
int _nbDeposits;
int _nbWithdrawals;
Account(void);
};
3.3 実装
// Account.cpp
#include "Account.hpp"
#include <iostream>
#include <iomanip>
#include <ctime>
// 静的メンバ変数の初期化
int Account::_nbAccounts = 0;
int Account::_totalAmount = 0;
int Account::_totalNbDeposits = 0;
int Account::_totalNbWithdrawals = 0;
// 静的メンバ関数
int Account::getNbAccounts(void) {
return _nbAccounts;
}
int Account::getTotalAmount(void) {
return _totalAmount;
}
int Account::getNbDeposits(void) {
return _totalNbDeposits;
}
int Account::getNbWithdrawals(void) {
return _totalNbWithdrawals;
}
void Account::displayAccountsInfos(void) {
_displayTimestamp();
std::cout << "accounts:" << _nbAccounts
<< ";total:" << _totalAmount
<< ";deposits:" << _totalNbDeposits
<< ";withdrawals:" << _totalNbWithdrawals
<< std::endl;
}
void Account::_displayTimestamp(void) {
std::time_t t = std::time(NULL);
std::tm* now = std::localtime(&t);
std::cout << "[" << (now->tm_year + 1900)
<< std::setfill('0')
<< std::setw(2) << (now->tm_mon + 1)
<< std::setw(2) << now->tm_mday
<< "_"
<< std::setw(2) << now->tm_hour
<< std::setw(2) << now->tm_min
<< std::setw(2) << now->tm_sec
<< "] ";
}
// コンストラクタ
Account::Account(int initial_deposit)
: _accountIndex(_nbAccounts), _amount(initial_deposit),
_nbDeposits(0), _nbWithdrawals(0) {
_nbAccounts++;
_totalAmount += initial_deposit;
_displayTimestamp();
std::cout << "index:" << _accountIndex
<< ";amount:" << _amount
<< ";created" << std::endl;
}
// デストラクタ
Account::~Account(void) {
_displayTimestamp();
std::cout << "index:" << _accountIndex
<< ";amount:" << _amount
<< ";closed" << std::endl;
}
// 入金
void Account::makeDeposit(int deposit) {
_displayTimestamp();
std::cout << "index:" << _accountIndex
<< ";p_amount:" << _amount
<< ";deposit:" << deposit;
_amount += deposit;
_nbDeposits++;
_totalAmount += deposit;
_totalNbDeposits++;
std::cout << ";amount:" << _amount
<< ";nb_deposits:" << _nbDeposits
<< std::endl;
}
// 出金
bool Account::makeWithdrawal(int withdrawal) {
_displayTimestamp();
std::cout << "index:" << _accountIndex
<< ";p_amount:" << _amount
<< ";withdrawal:";
if (withdrawal > _amount) {
std::cout << "refused" << std::endl;
return false;
}
_amount -= withdrawal;
_nbWithdrawals++;
_totalAmount -= withdrawal;
_totalNbWithdrawals++;
std::cout << withdrawal
<< ";amount:" << _amount
<< ";nb_withdrawals:" << _nbWithdrawals
<< std::endl;
return true;
}
int Account::checkAmount(void) const {
return _amount;
}
void Account::displayStatus(void) const {
_displayTimestamp();
std::cout << "index:" << _accountIndex
<< ";amount:" << _amount
<< ";deposits:" << _nbDeposits
<< ";withdrawals:" << _nbWithdrawals
<< std::endl;
}
3.4 ポイント解説
タイムスタンプの生成:
#include <ctime>
std::time_t t = std::time(NULL);
std::tm* now = std::localtime(&t);
// フォーマット: [YYYYMMDD_HHMMSS]
std::cout << "[" << (now->tm_year + 1900)
<< std::setfill('0')
<< std::setw(2) << (now->tm_mon + 1) // 月は0-11
<< std::setw(2) << now->tm_mday
<< "_"
<< std::setw(2) << now->tm_hour
<< std::setw(2) << now->tm_min
<< std::setw(2) << now->tm_sec
<< "] ";
静的メンバの初期化:
// ヘッダファイル
class Account {
static int _nbAccounts;
// ...
};
// ソースファイル(クラス外で必ず定義)
int Account::_nbAccounts = 0;
---
まとめ
本章で実装した内容:
- ex00: Megaphone
- ex01: PhoneBook
- ex02: Account
次章では、これらの実装のテストとデバッグ方法を学びます。