定例勉強会の活動報告。
今日9/28は後期の最初の活動でした。今日のテーマは「C++標準ライブラリの基本」と「連想配列」でした。
さて、まずは「C++標準ライブラリの基本」。どうやらメンバーのみんなは、競プロではまだCの関数を使っている様子(1人Javaもいましたが)。ここでC++に慣れてもらって、便利な機能をいろいろ使えるようになろうという次第。
標準入出力
最初は標準入出力。C++では iostream というライブラリを include して使います。
入力ではstd::cin 、出力では std::cout というものを用います(console in、console outの略とも。諸説あり)。書き方としては、
- #include<iostream>
- int main(){
- int hoge, piyo;
- std::cin >> hoge >> piyo;
- std::cout << hoge << " " << piyo << std::endl;
- /* Cで書くと、
- scanf("%d%d", &hoge, &piyo);
- printf("%d %d\n", hoge, piyo);
- */
- return 0;
- }
こんな感じ。std::endl は改行を表します。
この書き方のいいところは、変数の型を気にしなくていいところ。%dとかの出力変換指定子に悩まされることはないですね(もっとも、ちょっと凝ったことをやろうとすると、また必要になりますが)。cinをキーボード、coutをディスプレイだと思うと >> や << の演算子の向きも多分間違えないでしょう。
ところで、ちょくちょく出てきている std:: 、こいつはなんでしょう?これは名前空間と呼ばれるもので、同じ名前の関数どうしで衝突する(どれを参照しているのかわからなくなる)のを防いでくれたりするものです。競プロでは基本的に標準ライブラリしか使わないので、この場合に限っていえばこいつはコードを冗長にしてしまうだけです。
そこで、main関数の前に using namespace std; と書くと、それ以降、同一ファイル内で std:: を書かなくても関数を呼び出すことができるようになります。明らかに衝突する関数がない場合に有効ですね。
クラス
次は、CとC++との大きな違いであるクラスについて。
C++はもともと、Cにオブジェクト指向的な概念を取り入れた言語として開発されたものです。それを表現する方法として、クラスがあります。
オブジェクト指向の説明は他のサイトにおまかせするとして、ここでは human というクラスを考えてみましょう。human、つまりヒトをひとつのオブジェクトとして捉えると、ヒトには名前や年齢、性別などさまざまな情報が結びついています。また、話したり、歩いたりなど、さまざまな動作を行うことができます。この「情報」を格納するのがメンバ変数、「動作」を定義しているのがメンバ関数、またはメソッドです。
クラスのオブジェクトを生成するときは、 クラス名 オブジェクト名; という形で宣言します。それぞれのメンバ変数やメソッドにアクセスするには、オブジェクト名.メンバ変数 か オブジェクト名.メソッド と書きます。
さて、標準ライブラリでは、文字列をスマートに扱うための string クラスが定義されています。これを使ってみましょう。
- #include <iostream>
- using namespace std;
- int main(){
- string s; // s というstringクラス オブジェクトを生成
- cin >> s;
- cout << s << "\n";
- return 0;
- }
入力された文字列を表示するだけのシンプルなプログラムです。
stringクラスでは、配列のように文字列を1文字ずつ参照することが可能です。メソッドの使い方とあわせて確認してみましょう。
- #include <iostream>
- using namespace std;
- int main(){
- string s;
- cin >> s;
- for(int i=0; i<s.length(); i++){
- cout << s[i] << "\n";
- }
- return 0;
- }
1文字ずつ改行しながら表示します。length() は stringオブジェクトの長さを返すメソッドです。
stringの便利なメソッドについて、もう少し見てみましょう。
・find(検索文字列, 検索開始位置=0)
検索文字列が含まれていればその位置を(先頭から何文字目か、0から始まることに注意)、含まれていなければstd::string::npos という定数を返します。検索開始位置を省略すると先頭から検索します。
- #include <iostream>
- using namespace std;
- int main(){
- string s;
- s = "serval-chan";
- cout << s.find("chan") << "\n";
- s= "arai-san";
- cout << s.find("chan") << " " << string::npos << "\n";
- return 0;
- }
- /*--output--
- 7
- 4294967295 4294967295
- */
・substr(開始位置, 部分文字列の長さ)
開始位置からの部分文字列を返します。これは例を見てもらったほうが早そうですね。
- #include <iostream>
- using namespace std;
- int main(){
- string s;
- s = "serval-chan";
- cout << s.substr(0,6) << "\n";
- cout << s.substr(7) << "\n";
- return 0;
- }
- /*--output--
- serval
- chan
- */
長さを省略すると、開始位置から最後までの文字列が返ってきます。sは変化しません。文字数より大きな開始位置を指定すると例外を投げます。
演習
さて、ここまでわかれば簡単な文字列操作の問題も解けますね。
今日の問題(1)はこれでした。
与えられる文字列の先頭が”YAKI”であるかどうかを判定するだけです。上に挙げたメソッドをうまく使えば、さくっと解くことができます。
解答例:
- #include<iostream>
- using namespace std;
- int main(){
- string s;
- cin >> s ;
- if(s.find("YAKI")==0) cout << "Yes" << "\n";
- else cout << "No" << "\n";
- return 0;
- }
今日はさらにもう一本。「連想配列」では、vectorの扱いとmapの仕組みについてなどを勉強しました。