このページは、練習問題の解答例や解説のページです。
次のプログラムの実行結果はどうなりますか?
#include <iostream>
void f1(int n);
void f2(int n1, int n2);
int main()
{
(1);
f1(10, 20);
f2}
void f1(int n)
{
(n, n + 1);
f2}
void f2(int n1, int n2)
{
std::cout << n1 << ", " << n2 << "\n";
}
関数がいくつあって、どこに書かれていようとも、プログラムの開始は main関数からというルールは変わりません。そのため、まずは f1(1);
が実行され、f1関数に進みます。このとき実引数の 1
が渡され、f1関数の仮引数n の値になります(本編解説)。
f1関数の定義をみると、f2(n, n + 1);
とあります。したがって今度は f2関数へ進みます。このとき、実引数の n
と n + 1
を計算した値が、f2関数の仮引数n1、n2 の値になります。n の値は 1 なので、1 と 2 が渡されます。
f2関数は n1 と n2 の値を出力しています。それぞれ 1 と 2 ですから、出力結果は 1, 2
です。
f2関数にはこれ以上処理がありませんから、呼び出し元に戻ります(本編解説)。戻った先の f1関数にもこれ以上の処理がありませんから、さらに呼び出し元に戻ります。戻り先は main関数内の f1(1);
の直後です。f2(10, 20);
があるので、次は f2関数に進みます。今回の実引数は 10
と 20
です。
f2関数では n1 と n2 の値を出力します。今回は 10
と 20
なので、出力結果は 10, 20
です。
f2関数にはこれ以上処理がありませんから、呼び出し元に戻ります。今回は main関数から直接来たので、戻り先も main関数です。main関数にはこれ以上処理がありませんから、これで main関数も終了となり、プログラムの実行が終わります。
最終的に出力結果はこうです。
1, 2
10, 20
このような処理の流れは、Visual Studio のステップ実行の機能を使って確認できます。「Visual Studio編>ステップ実行」のページで解説している、ステップインを使ってみるといいでしょう。
std::vector<int> の要素の値を出力する関数を作成してください。すべての値を1行に、,
で区切りながら出力するようにします。
たとえば、要素が 1, 2, 3, 4, 5
であれば、次のように出力します。
1, 2, 3, 4, 5
このような実行結果を得るプログラムは意外とやっかいです。というのも、末尾の要素の後ろには ,
を付けたくないため、どうしてもこの部分を特別扱いしたコードを書かなければならないためです。
色々とやりようはありますが、たとえば次のように書けます。
#include <iostream>
#include <vector>
void print_int_vector(const std::vector<int>& vec);
int main()
{
std::vector<int> v1 {1, 2, 3, 4, 5};
std::vector<int> v2 {};
std::vector<int> v3(3, 7);
(v1);
print_int_vector(v2);
print_int_vector(v3);
print_int_vector}
void print_int_vector(const std::vector<int>& vec)
{
bool isFirst = true;
for (auto e : vec) {
if (!isFirst) {
std::cout << ", ";
}
std::cout << e;
= false;
isFirst }
std::cout << "\n";
}
実行結果:
1, 2, 3, 4, 5
7, 7, 7
最初の要素のときにだけ true になる変数isFirst を用意して、最初の要素以外のときにだけ ,
を出力するようにしています。
コードが書きづらいですし、無駄も大きくなるため、出力の形を工夫することも考えていいでしょう。たとえば、末尾に ,
が付くことを許していいかもしれませんし、単に空白文字で区切るようにすれば、末尾に空白が出力されていても気付かれないかもしれません。空白区切りなら次のように簡単に書けます。
void print_int_vector(const std::vector<int>& vec)
{
for (auto e : vec) {
std::cout << e << " ";
}
std::cout << "\n";
}
実行結果:
1 2 3 4 5
7 7 7
std::vector<int> に複数の要素を追加する関数を作成してください。次のように呼び出せるようにしてください。
std::vector<int> v {};
(v, {0, 1, 2, 3, 4}); push_back_values
push_back_values関数の1つ目の実引数は v
であり、これは std::vector<int>型です。要素を追加する関数だということは、書き換えが必要ということですから、1つ目の仮引数の型は std::vector<int>&
だということになります(本編解説)。
2つ目の実引数は {0, 1, 2, 3, 4}
で、これは要素が int型の初期化子リストですから、2つ目の仮引数は std::initializer_list<int>
ということになります(本編解説)。
したがって、次のように書けます。
#include <initializer_list>
#include <iostream>
#include <vector>
void push_back_values(std::vector<int>& vec, std::initializer_list<int> list);
void print_int_vector(const std::vector<int>& vec);
int main()
{
std::vector<int> v {};
(v, {0, 1, 2, 3, 4});
push_back_values(v);
print_int_vector}
void push_back_values(std::vector<int>& vec, std::initializer_list<int> list)
{
for (auto e : list) {
.push_back(e);
vec}
}
void print_int_vector(const std::vector<int>& vec)
{
bool isFirst = true;
for (auto e : vec) {
if (!isFirst) {
std::cout << ", ";
}
std::cout << e;
= false;
isFirst }
std::cout << "\n";
}
実行結果:
0, 1, 2, 3, 4
print_int_vector関数は、問題② で作ったものを流用しています。
std::vector<int> の要素から、指定の値を探して、指定の値に置き換える関数を作成してください。
たとえば次のように書けます。
#include <iostream>
#include <vector>
void replace_int_vector_value(std::vector<int>& vec, int target_value, int new_value);
void print_int_vector(const std::vector<int>& vec);
int main()
{
std::vector<int> v1 {1, 2, 2, 3, 2, 4};
std::vector<int> v2 {};
(v1, 2, 10);
replace_int_vector_value(v2, 2, 10);
replace_int_vector_value
(v1);
print_int_vector(v2);
print_int_vector}
void replace_int_vector_value(std::vector<int>& vec, int target_value, int new_value)
{
for (auto it = std::begin(vec); it != std::end(vec); ++it) {
if (*it == target_value) {
*it = new_value;
}
}
}
void print_int_vector(const std::vector<int>& vec)
{
bool isFirst = true;
for (auto e : vec) {
if (!isFirst) {
std::cout << ", ";
}
std::cout << e;
= false;
isFirst }
std::cout << "\n";
}
実行結果:
1, 10, 10, 3, 10, 4
print_int_vector関数は、問題② で作ったものを流用しています。
値の置き換えをおこなう replace_int_vector_value関数の仮引数は、std::vector<int>型の値を受け取るための vec と、対象の値を受け取る target_value、置き換え後の値を受け取る new_value の3つです。関数内で値を変更しなければならないので、vec は参照型にしなければなりません。
仮引数vec は参照型ですから、vec は replace_int_vector_value関数を呼び出すときに実引数に指定した std::vector<int>型の変数の別名であり、同一体です。そのため、std::begin(vec)
や std::end(vec)
によって得られるイテレータは、呼び出し元にある std::vector<int>型の変数から直接得られるものと同じです。したがって、*it = new_vector
によって、呼び出し元にある std::vector<int> の要素の値が書き換わります。
蔵書リストのプログラムには、本の情報を出力しているコードが何か所かに存在しています。この部分を1つの関数に共通化してください。
蔵書リストのプログラムのソースコードは、前のページの練習問題の解答ページにあります。
問題のコードは、以下の5行です。これが何度も登場しています。
std::cout << "書名: " << book.title << "\n"
<< "著者名: " << book.author << "\n"
<< "価格: " << book.price << "\n"
<< "評価: " << book.evaluate << "\n"
<< "\n";
これを関数にまとめます。book.title
など、Book構造体のデータメンバにアクセスできる必要があるので、引数で Book型の値を受け取れるようにしましょう。構造体のサイズは大きいので、参照を使います(本編解説)。また、関数内では書き換えないので、const参照にします(本編解説)。
仮引数のところで Book
が登場することになるため、Book構造体の定義を、ソースファイルの上の方に持ってくる必要もあります(本編解説)。
結果、ソースコード全体は次のようになります。
#include <algorithm>
#include <fstream>
#include <iostream>
#include <sstream>
#include <string>
#include <vector>
struct Book {
std::string title {}; // 書名
std::string author {}; // 著者名
int price {}; // 価格
int evaluate {}; // 評価
};
void print_book_data(const Book& book)
{
std::cout << "書名: " << book.title << "\n"
<< "著者名: " << book.author << "\n"
<< "価格: " << book.price << "\n"
<< "評価: " << book.evaluate << "\n"
<< "\n";
}
int main()
{
constexpr auto booklist_path = "booklist.txt";
std::vector<Book> books {};
bool isChanged {false};
while (true) {
std::cout << "----------------------------\n"
<< "コマンドを入力してください。\n"
<< "a: 本を登録する。\n"
<< "d: 本を削除する。\n"
<< "c: すべての本を削除する。\n"
<< "p: リストを確認する。\n"
<< "st: 書名で検索する。\n"
<< "sa: 著者名で検索する。\n"
<< "sp: 価格で検索する。\n"
<< "se: 評価で検索する。\n"
<< "r: ファイルから読み込む。\n"
<< "w: ファイルに書き込む。\n"
<< "e: 終了する。\n"
<< "----------------------------\n";
std::string command {};
std::getline(std::cin, command);
if (command == "a") {
// 本を登録する
{};
Book book std::istringstream iss {};
std::string tmp {};
std::cout << "書名を入力してください。\n";
std::getline(std::cin, book.title);
std::cout << "著者名を入力してください。\n";
std::getline(std::cin, book.author);
std::cout << "価格を入力してください。\n";
while (true) {
std::getline(std::cin, tmp);
.str(tmp);
iss>> book.price;
iss if (!iss || book.price < 0) {
std::cout << "価格は 0以上の整数で入力してください。\n";
.clear();
iss}
else {
break;
}
}
std::cout << "評価を入力してください。\n";
std::getline(std::cin, tmp);
.clear();
iss.str(tmp);
iss>> book.evaluate;
iss if (!iss) {
std::cout << "入力内容に不備があります。\n";
}
else {
.push_back(book);
books= true;
isChanged std::cout << "登録しました。\n";
}
}
else if (command == "d") {
// 本を削除する
std::cout << "書名を入力してください。\n";
std::string title {};
std::getline(std::cin, title);
auto it = std::find_if(std::cbegin(books), std::cend(books), [title](Book e){ return e.title == title; });
if (it == std::cend(books)) {
std::cout << "指定された本は登録されていません。\n";
}
else {
.erase(it);
books= true;
isChanged std::cout << "削除しました。\n";
}
}
else if (command == "c") {
// すべての本を削除する
.clear();
books= true;
isChanged std::cout << "削除しました。\n";
}
else if (command == "p") {
// リストを確認する
for (auto& book : books) {
(book);
print_book_data}
}
else if (command == "st") {
// 書名で検索する
std::cout << "書名を入力してください。\n";
std::string title {};
std::getline(std::cin, title);
for (auto& book : books) {
if (book.title == title) {
(book);
print_book_data}
}
}
else if (command == "sa") {
// 著者名で検索する
std::cout << "著者名を入力してください。\n";
std::string author {};
std::getline(std::cin, author);
for (auto& book : books) {
if (book.author == author) {
(book);
print_book_data}
}
}
else if (command == "sp") {
// 価格で検索する
std::cout << "価格を入力してください。\n";
std::string tmp {};
std::getline(std::cin, tmp);
std::istringstream iss {tmp};
int price {};
>> price;
iss if (!iss || price < 0) {
std::cout << "入力内容に不備があります。\n";
}
else {
for (auto& book : books) {
if (book.price >= price) {
(book);
print_book_data}
}
}
}
else if (command == "se") {
// 評価で検索する
std::cout << "評価を入力してください。\n";
std::string tmp {};
std::getline(std::cin, tmp);
std::istringstream iss {tmp};
int evaluate {};
>> evaluate;
iss if (!iss) {
std::cout << "入力内容に不備があります。\n";
}
else {
for (auto& book : books) {
if (book.evaluate >= evaluate) {
(book);
print_book_data}
}
}
}
else if (command == "r") {
// ファイルから読み込む
std::ifstream ifs(booklist_path);
if (!ifs) {
std::cout << "ファイルが開けません。\n";
continue;
}
// メモリ上の本のデータを消す
.clear();
books
while (!ifs.eof()) {
{};
Book book std::istringstream iss {};
std::string tmp {};
std::getline(ifs, book.title);
if (book.title.empty()) {
break;
}
std::getline(ifs, book.author);
std::getline(ifs, tmp);
.str(tmp);
iss>> book.price;
iss .clear();
iss
std::getline(ifs, tmp);
.str(tmp);
iss>> book.evaluate;
iss
.push_back(book);
books}
= false;
isChanged std::cout << "読み込みました。\n";
}
else if (command == "w") {
// ファイルに書き込む
std::ofstream ofs(booklist_path);
if (!ofs) {
std::cout << "ファイルが開けません。\n";
continue;
}
for (auto& book : books) {
<< book.title << "\n"
ofs << book.author << "\n"
<< book.price << "\n"
<< book.evaluate << "\n";
}
.close();
ofsif (!ofs) {
std::cout << "書き込みに失敗しました。\n";
continue;
}
= false;
isChanged std::cout << "書き込みました。\n";
}
else if (command == "e") {
// 終了する
if (isChanged) {
std::cout << "変更した内容をファイルに書き込んでいませんが、終了してよろしいですか? (Y/N)\n";
std::string s {};
std::cin >> s;
if (s != "Y" && s != "y") {
continue;
}
}
std::cout << "終了します。\n";
return 0;
}
else {
// 無効なコマンド
continue;
}
}
}
はてなブックマーク に保存 | Pocket に保存 | Facebook でシェア |
X で ポスト/フォロー | LINE で送る | noteで書く |
![]() |
管理者情報 | プライバシーポリシー |