string 解答ページ | Programming Place Plus C++編【標準ライブラリ】 第2章

トップページC++編

C++編で扱っている C++ は 2003年に登場した C++03 という、とても古いバージョンのものです。C++ はその後、C++11 -> C++14 -> C++17 -> C++20 -> C++23 と更新されています。
なかでも C++11 での更新は非常に大きなものであり、これから C++ の学習を始めるのなら、C++11 よりも古いバージョンを対象にするべきではありません。特に事情がないなら、新しい C++ を学んでください。 当サイトでは、C++14 をベースにした新C++編を作成中です。

問題① 🔗

問題① std::basic_string型の変数を ‘X’ という1文字で初期化するには、どうすればいいですか?


1文字で初期化するには ‘X’ ではなく、“X” のように文字列として指定する必要があります。

#include <iostream>
#include <string>

int main()
{
    std::string str("X");

    std::cout << str << std::endl;
}

実行結果:

X

問題② 🔗

問題② あなたの使っているコンパイラにおいて、空の std::basic_string はどれだけのサイズと容量を持つか調べてみてください。


サイズは size関数、容量は capacity関数で取得できます。また、std::basic_string型の変数を宣言する際、初期値を与えなければ、空の文字列になります。

#include <iostream>
#include <string>

int main()
{
    std::string str;

    std::cout << "size: " << str.size() << std::endl;
    std::cout << "capa: " << str.capacity() << std::endl;
}

実行結果:

size: 0
capa: 15

環境によって、容量は変わり得ますが、サイズの方は必ず 0 のはずです。容量が変わるのは、std::basic_string の具体的な実装方法がライブラリ製作者の裁量にある程度任されているためです。

上記の実行結果は、Visual Studio 2017 でのものです。この環境では、容量は 15 になっています。これは、多くの場合、std::basic_string に与えられる文字列は短いものであり(世界中のプログラムの経験則から来ている判断です)、そういった「よくある状況」では、時間のかかる動的メモリ割り当てをできるだけ避けようとしているからです。

問題③ 🔗

問題③ 文字列の途中に ‘\0’ を挿入することによって、std::basic_string のサイズは変化するでしょうか?


#include <iostream>
#include <string>

int main()
{
    std::string str("abcde");
    std::cout << "size: " << str.size() << std::endl;

    str[2] = '\0';
    std::cout << "size: " << str.size() << std::endl;
}

実行結果:

size: 5
size: 5

最初、“abcde” という文字列を格納してあり、このときのサイズは 5 です。その後、str[2] に ‘\0’ を代入してからサイズを調べてみると、やはり 5 のままです。

C言語でも C++ でも、生の文字列の終端は ‘\0’ です。strlen関数が返す長さは、‘\0’ までの長さですが、std::basic_string の「サイズ」の意味は違っています。途中に ‘\0’ があるとしても、std::basic_string にとっては “ab\0de” であって、これは全体として「サイズ=5」なのです。

問題④ 🔗

問題④ std::basic_string を渡すと、その中に含まれる小文字のアルファベットを大文字化するような関数を作成してください(ちなみに、std::basic_string にそのような機能はありません)。


ここでは、C言語の toupper関数を利用します。C++ なので、標準ヘッダの名前は <cctype>、関数は正確には std::toupper になります。

#include <iostream>
#include <string>
#include <cctype>

void to_upper(std::string* s);

int main()
{
    std::string str("abcABC012");

    to_upper(&str);
    std::cout << str << std::endl;
}

void to_upper(std::string* s)
{
    for (std::string::size_type i = 0; i < s->size(); ++i) {
        (*s)[i] = std::toupper((*s)[i]);
    }
}

実行結果:

ABCABC012

添字を使って、先頭から末尾までループさせています。末尾の添字は、サイズの値に一致しますから、size関数で取得した値で判定すればいいです。

to_upper関数に、std::basic_string型のポインタを渡していますが、C++ では、参照を使った方がいいかもしれません(【言語解説】第16章)。

#include <iostream>
#include <string>
#include <cctype>

void to_upper(std::string& s);

int main()
{
    std::string str("abcABC012");

    to_upper(str);
    std::cout << str << std::endl;
}

void to_upper(std::string& s)
{
    for (std::string::size_type i = 0; i < s.size(); ++i) {
        s[i] = std::toupper(s[i]);
    }
}

実行結果:

ABCABC012

問題⑤ 🔗

問題⑤ 問題④の関数を、イテレータによる範囲指定に対応させてください。


2つのイテレータを渡して、それが範囲を示すものとします。

#include <iostream>
#include <string>
#include <cctype>

void to_upper(std::string::iterator begin, std::string::iterator end);

int main()
{
    std::string str("abcABCabc");

    to_upper(str.begin(), str.begin() + 7);
    std::cout << str << std::endl;
}

void to_upper(std::string::iterator begin, std::string::iterator end)
{
    for (std::string::iterator it = begin;
         it != end;
         ++it
    ) {
        *it = std::toupper(*it);
    }
}

実行結果:

ABCABCAbc

STL での共通ルールとして、範囲の終わりを表すイテレータは、有効な末尾の要素の「1つ先」を参照するものとします。


参考リンク 🔗


更新履歴 🔗

 VisualStudio 2015 の対応終了。

 全体的に見直し修正。

 VisualStudio 2013 の対応終了。

 「VisualC++」という表現を「VisualStudio」に統一。

 VisualC++ 2017 に対応。

≪さらに古い更新履歴≫

 VisualC++ 2012 の対応終了。

 VisualC++ 2010 の対応終了。

 VisualC++ 2015 に対応。

 問題①の解説内の誤った記述を削除。

 VisualC++ 2013 に対応。

 VisualC++2008 の対応終了。

 新規作成。



第2章のメインページへ

C++編のトップページへ

Programming Place Plus のトップページへ



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