std::vector 解答ページ | Programming Place Plus 新C++編

トップページ新C++編std::vector

このページの概要

このページは、練習問題の解答例や解説のページです。



解答・解説

問題1 (確認★)

次の変数宣言のうち、エラーになるものをすべて選んでください。

  1. std::vector<int> v;
  2. std::vector<int> v {10};
  3. std::vector<int> v {};
  4. std::vector<int> v();
  5. std::vector<int> v {2.0, 2.5, 3.0};
  6. std::vector<int> v = (10, 20, 30);
  7. std::vector<std::string> v(10, "Hello");


1番は、要素なしの状態に初期化されます。std::vector は、int型などのシンプルな型と違い、初期化子なしの場合にもきちんと初期化されます。

2番は、10 という値をもった1つの要素で初期化されます。{} を使った初期化はリスト初期化と呼ばれ、{} の中に書き並べた式の結果が、各要素の初期値になります。

3番は、要素なしの状態に初期化されます。この構文は2番と同じものですが、{} の中が空の場合は、値初期化というルールで初期化されることになっています。

4番は、要素なしの状態に初期化されます。() を使った初期化は、コンストラクタを使う方法です。std::vector の場合、この構文は要素がない状態で初期化することを意味しています。

5番は、コンパイルエラーです。{} を使った初期化では暗黙の縮小変換が禁止されますが、{} の内側には double型の値が並んでおり、std::vector の要素の型は int型なので、double型から int型への縮小変換が必要です。

6番は、コンパイルエラーです。() を使った初期化では、= を挟むことができません。

7番は、“Hello” という値をもった 10個の要素で初期化されます。

ということで、エラーになるのは、5番と6番です。

全体的なルールは、本編解説を参照してください。

問題2 (基本★)

標準入力から整数を5つ入力させ、std::vector に格納するプログラムを作成してください。


たとえば次のようになります。

#include <iostream>
#include <vector>

int main()
{
    constexpr auto element_count = 5;
    std::vector<int> v(element_count);

    for (int i = 0; i < element_count; ++i) {
        std::cout << "Please enter the integer.\n";
        std::cin >> v.at(i);
    }

    for (int e : v) {
        std::cout << e << "\n";
    }
}

実行結果:

Please enter the integer.
3  <-- 入力した整数
Please enter the integer.
4  <-- 入力した整数
Please enter the integer.
5  <-- 入力した整数
Please enter the integer.
6  <-- 入力した整数
Please enter the integer.
7  <-- 入力した整数
3
4
5
6
7

まず、std::vector の変数を宣言します。5つの入力を受け取るつもりなので、要素数を指定できる初期化方法を使って、std::vector<int> v(element_count); のように宣言します(本編解説)。要素数の 5 という値が複数回登場するので、constexpr変数にしておきました。

std::cin で標準入力から値を受け取ります。これまでのページのように、int型の変数に受け取ってもいいですが、std::cin >> v.at(i) のようにして、std::vector の要素へ直接書き込んでも問題ありません。std::cin >> v[i]; でも同じ結果になります(本編解説)。

最後に、各要素の値を出力して確認しています。すべての要素に用があるので、範囲for文の使いどころです(本編解説)。

問題3 (基本★)

問題2のプログラムを改造して、各要素が奇数か偶数かに応じて、“odd”、“even” のいずれかを標準出力へ出力するようにしてください。

たとえば、{7, -2, 5, 2, 3} という要素が入っているなら、oddevenoddevenodd を出力します。


範囲for文の内側で if文を使って分岐させればいいです。

#include <iostream>
#include <vector>

int main()
{
    constexpr auto element_count = 5;
    std::vector<int> v(element_count);

    for (int i = 0; i < element_count; ++i) {
        std::cout << "Please enter the integer.\n";
        std::cin >> v.at(i);
    }

    for (int e : v) {
        if (e % 2 == 0) {
            std::cout << "even\n";
        }
        else {
            std::cout << "odd\n";
        }
    }
}

実行結果:

Please enter the integer.
7  <-- 入力した整数
Please enter the integer.
-2  <-- 入力した整数
Please enter the integer.
5  <-- 入力した整数
Please enter the integer.
2  <-- 入力した整数
Please enter the integer.
3  <-- 入力した整数
odd
even
odd
even
odd

問題4 (応用★★)

標準入力から文字列を5つ入力させ、std::vector に格納したあと、入力された順番とは逆の順番で出力するプログラムを作成してください。


たとえば次のようになります。

#include <iostream>
#include <string>
#include <vector>

int main()
{
    constexpr auto element_count = 5;
    std::vector<std::string> v(element_count);

    for (int i = 0; i < element_count; ++i) {
        std::cout << "Please enter the string.\n";
        std::cin >> v.at(i);
    }

    for (int i = element_count - 1; i >= 0; --i) {
        std::cout << v.at(i) << "\n";
    }
}

実行結果:

Please enter the string.
aaa  <-- 入力した文字列
Please enter the string.
bbbb  <-- 入力した文字列
Please enter the string.
ccccc  <-- 入力した文字列
Please enter the string.
dddd  <-- 入力した文字列
Please enter the string.
eee  <-- 入力した文字列
eee
dddd
ccccc
bbbb
aaa

今回は、文字列を扱いたいので、std::vector<std::string> の変数を宣言します。

標準入力から文字列を受け取って、std::vector に入れていく方法は、int型のときと変わりません。

逆の順番で出力するところですが、範囲for文では、先頭から末尾へという順番にしか対応できないため、通常の for文を使うことになります。「末尾の添字は、要素数 - 1」であることに注意してください。

【上級】こういうところで範囲外アクセスしてしまいやすいので、at関数を使っておくと安全ですが、添字が登場しない方法を使うことがもっとも安全であるといえます。たとえば、逆イテレータ1を使うとか(「イテレータ」のページを参照)、std::vector の要素を書き換えて構わないのなら std::reverse()2 で std::vector の中身を逆順にしてから範囲for文を使うといった方法があります。C++20 からは、std::views::reverse3 を使うとより簡単です。

問題5 (応用★★★)

標準入力から整数を5つ入力させ、std::vector<int> に格納したあと、各要素を以下のルールで変換して、std::vector<std::string> に格納するプログラムを作成してください。


このルールは、いわゆる Fizz Buzz と呼ばれる遊びを元にしたもので、プログラミングの練習課題や、最低限の実力を判定する題材としてよく登場するものです4

この問題では、単に文字列に変換したものを出力するのでなく、いったん、std::vector<std::string> に格納するという手順を踏ませています。つまり、要素の型が異なっている std::vector へデータを変換したうえで書き写す必要があるということです。

プログラムはたとえば次のように書けます。

#include <iostream>
#include <sstream>
#include <string>
#include <vector>

int main()
{
    constexpr auto element_count = 5;
    std::vector<int> v(element_count);
    std::vector<std::string> strings(element_count);

    for (int i = 0; i < element_count; ++i) {
        std::cout << "Please enter the integer.\n";
        std::cin >> v.at(i);
    }

    for (int i = 0; i < element_count; ++i) {
        if (v.at(i) % 15 == 0) {
            strings.at(i) = "Fizz Buzz";
        }
        else if (v.at(i) % 3 == 0) {
            strings.at(i) = "Fizz";
        }
        else if (v.at(i) % 5 == 0) {
            strings.at(i) = "Buzz";
        }
        else {
            std::ostringstream oss {};
            oss << v.at(i);
            strings.at(i) = oss.str();
        }
    }

    for (std::string s : strings) {
        std::cout << s << "\n";
    }
}

実行結果:

Please enter the string.
5  <-- 入力した文字列
Please enter the string.
8  <-- 入力した文字列
Please enter the string.
12  <-- 入力した文字列
Please enter the string.
15  <-- 入力した文字列
Please enter the string.
-1  <-- 入力した文字列
Buzz
8
Fizz
Fizz Buzz
-1

整数を格納するための std::vector<int> と、変換結果を格納するための std::vector<std::string> を、同じ要素数で定義しておきます。ここまでの練習問題と同様の方法で整数を受け取り終えたら、変換の処理に移ります。

15、3、5 の各倍数に該当するかどうかを調べればいいわけで、これは剰余演算子 (‘%’) で余りが出ないことを確認すればわかります。調べる順番が重要で、15 の倍数である数は、3 の倍数でも 5 の倍数でもあるので、15 の倍数であるかどうかを先に調べなければなりません。

いずれの倍数でもない場合、4 を “4” に、-1 を “-1” にするような変換が必要です。これは、std::ostringstream を利用すれば可能です(「stringstream」のページを参照)。

なお、この変換処理の部分では範囲for文でなく、通常の for文になっています。これは、2つの std::vector の同じ位置にアクセスする必要があるためです。範囲for文では、一つの std::vector の要素を順番にアクセスしていくことしかできず、他方の std::vector の対応する位置をうまく知ることができません。添字が必要です。


参考リンク



更新履歴




はてなブックマーク に保存 Pocket に保存 Facebook でシェア
X で ポストフォロー LINE で送る noteで書く
rss1.0 取得ボタン RSS 管理者情報 プライバシーポリシー
先頭へ戻る