プログラミング言語を整える
初級C++ を整える。
= C にクラスやテンプレートを足した C++ を、C との違いから学ぶ
C++ が初めての方は、先に そもそも C++ とは・どんな場面で使うか をどうぞ。
C++とは
C言語にクラスやテンプレートを加えた言語で、速さが求められるソフトウェアで長く使われています。
| 誕生 | 1983年(1979年に「C with Classes」として開発が始まりました) |
|---|---|
| 作った人・組織 | ビャーネ・ストロヴストルップ(ベル研究所) |
| 型の付け方 | 静的型付け |
| 動かし方 | コンパイルして機械語の実行ファイルを作る |
| ファイルの拡張子 | .cpp(ヘッダーは .h や .hpp) |
| 手元で動かすには | g++ -std=c++17 main.cpp -o main && ./main |
日本での使われ方
- 家電・自動車・産業機器などの組み込みソフトウェアで、C言語と並んで使われています。
- ゲーム開発で使われます。ゲームエンジンの Unreal Engine は C++ で書かれていて、ゲームの処理も C++ で書けます。
- 工場の検査装置などで使われる画像処理では、C++ で書かれたライブラリの OpenCV がよく使われます。
- 金融の取引システムのうち、処理の遅れを少なくしたい部分で使われることがあります。
- Windows のデスクトップアプリや、ブラウザ・データベースなどの大きなソフトウェアの中身にも使われています。
新人研修での位置づけ
新人研修では、C言語を学んだあとに「クラスを使ったプログラムの書き方」として扱われることがあります。組み込み機器やゲームを作る会社では、C++ そのものを研修の中心にする場合もあります。
関わる資格・試験
- 基本情報技術者試験(特定の言語に限らない国家試験です)
- エンベデッドシステムスペシャリスト試験(組み込み分野の国家試験です)
そもそも C++ とは
C 言語に「部品のまとめ方」を足して、大きなソフトも速く動かせる言語。
C++は、ベル研究所のビャーネ・ストロヴストルップが C 言語を拡張して作った言語です。C のコンパイル型という特徴を受け継ぎ、クラスやテンプレートといった、大きなプログラムを整理して書くための仕組みが加わっています。
Java や C# のように仮想マシンやランタイムを介さず、機械語の実行ファイルを直接作れるため、速さが求められる場面で選ばれます。C の書き方を引き継いでいますが、別の言語として規格が作られています。
どこで・どんな場面で使うか
-
ゲーム開発
ゲームエンジンの Unreal Engine は C++ で書かれていて、ゲームの処理も C++ で書けます。
-
組み込み・車載ソフト
家電や自動車、産業機器など、速さと省メモリが求められる機器で C と並んで使われます。
-
画像処理・映像処理
工場の検査装置などで使われる画像処理ライブラリの OpenCV は C++ で書かれています。
-
速さが重視される大きなソフト
処理の重い部分を C++ で書くことがあります。
-
求人での位置づけ
ゲーム・組み込み・制御系の求人で、C++ の経験を条件に挙げているものがあります。
向いている場面・向いていない場面
- 向いている:速さやメモリ使用量が大事なソフト。処理の細かい制御ができるので、ゲームや機器制御など、性能を詰めたい場面で力を発揮します。
- 向いている:C の知識を活かしたいとき。C の経験があれば、書き方の土台を使いながら、クラスなどの新しい仕組みを足して学べます。
- 向いていない:短い業務ツールをさっと作りたいとき。覚えることが多く、コンパイルも必要なので、小さな自動化には Python や VBA のほうが手軽なことがあります。
下のお題やクイズで、C と見比べながら C++ のコード例を読んでみてください。
5つのお題をC++で書く
C・C++・Java・C#・COBOL・VBA の6ページで、同じ5つのお題を同じ順に並べています。言語を切り替えると、書き方の違いがそのまま比べられます。
このページではコードを実行できません。表示している出力とエラーのメッセージは、g++ 13.3.0 で実際にコンパイル・実行して、一字一句同じになることを確かめたものです。
コード(.cpp)
#include <iostream>
int main() {
std::cout << "Hello, World!" << std::endl;
return 0;
}
実行すると
Hello, World!
画面への出力は std::cout に << で流し込みます。std::endl は改行を出して、出力をすぐ画面に送ります。
コード(.cpp)
#include <iostream>
int main() {
for (int i = 1; i <= 15; i++) {
if (i % 15 == 0) {
std::cout << "FizzBuzz" << std::endl;
} else if (i % 3 == 0) {
std::cout << "Fizz" << std::endl;
} else if (i % 5 == 0) {
std::cout << "Buzz" << std::endl;
} else {
std::cout << i << std::endl;
}
}
return 0;
}
実行すると
1 2 Fizz 4 Buzz Fizz 7 8 Fizz Buzz 11 Fizz 13 14 FizzBuzz
for や if の書き方は C言語と同じです。std::cout は数値も文字列も同じ << で出力できます。
コード(.cpp)
#include <iomanip>
#include <iostream>
#include <vector>
int main() {
std::vector<int> numbers{3, 1, 4, 1, 5, 9, 2, 6};
int total = 0;
for (int n : numbers) {
total += n;
}
double average = static_cast<double>(total) / numbers.size();
std::cout << "合計: " << total << std::endl;
std::cout << "平均: " << std::fixed << std::setprecision(2) << average << std::endl;
return 0;
}
実行すると
合計: 31 平均: 3.88
std::vector は要素の数をあとから増やせる配列で、size() で個数がわかります。int どうしの割り算は小数が切り捨てられるため、static_cast<double> で小数に変えてから割ります。
コード(.cpp)
#include <fstream>
#include <iostream>
#include <string>
int main() {
{
std::ofstream out("memo.txt");
out << "1行目: こんにちは" << std::endl;
out << "2行目: C++" << std::endl;
}
std::ifstream in("memo.txt");
std::string line;
while (std::getline(in, line)) {
std::cout << line << std::endl;
}
return 0;
}
実行すると
1行目: こんにちは 2行目: C++
std::ofstream は書き込み用、std::ifstream は読み込み用のファイルです。{ } のブロックを抜けると ofstream が自動で閉じられるため、close() を書かなくても読み込む前に内容が保存されます。
コード(.cpp)
#include <iostream>
#include <stdexcept>
int divide(int a, int b) {
if (b == 0) {
throw std::runtime_error("0で割ることはできません");
}
return a / b;
}
int main() {
try {
std::cout << divide(10, 2) << std::endl;
std::cout << divide(10, 0) << std::endl;
} catch (const std::runtime_error& e) {
std::cout << "エラー: " << e.what() << std::endl;
}
return 0;
}
実行すると
5 エラー: 0で割ることはできません
C++ では整数を0で割っても例外は自動では起きず、動きが決まっていません(未定義動作)。そのため割る前に自分で調べて throw し、try と catch で受け止めます。
出力当てクイズ
コードを読んで、画面に出るものを選んでください。選ぶと正解と理由が出ます(答えはどこにも送られず、記録もしません)。
問1
#include <iostream>
int main() {
std::cout << 7 / 2 << " " << 7 % 2 << std::endl;
return 0;
}
画面に出るのは?
int どうしの割り算は小数点以下が切り捨てられるため、7 / 2 は 3 です。% は割り算の余りなので 1 になります。
問2
#include <iostream>
#include <string>
int main() {
std::string a = "A";
std::string b = a;
std::string& r = a;
b += "B";
r += "C";
std::cout << a << " " << b << std::endl;
return 0;
}
画面に出るのは?
b = a はコピーなので、b を変えても a は変わりません。r は a の別名(参照)なので、r に "C" をつなげると a が "AC" になります。
問3
#include <iostream>
#include <vector>
int main() {
std::vector<int> v(3);
v.push_back(5);
std::cout << v.size() << " " << v[0] << " " << v.back() << std::endl;
return 0;
}
画面に出るのは?
std::vector<int> v(3) は 0 が3つ入った vector を作ります。push_back で末尾に 5 が足されるので、個数は 4、先頭は 0、末尾は 5 です。
問4
#include <iostream>
#include <string>
std::string f(int) { return "int"; }
std::string f(double) { return "double"; }
int main() {
std::cout << f(1) << " " << f(1.0) << " " << f('a') << std::endl;
return 0;
}
画面に出るのは?
同じ名前の関数を引数の型ごとに作れます(オーバーロード)。1.0 は double なので f(double) が選ばれます。文字の 'a' は char で、double への変換より int への格上げが優先されるため f(int) が呼ばれます。
よくあるエラーと直し方
初めて書く人が出会いやすいエラーです。メッセージは英語のまま出ることが多いので、どの言葉を手がかりにすればよいかもあわせて載せています。
コンパイル時 セミコロンの付け忘れ
エラーになるコード
#include <iostream>
int main() {
std::cout << "Hello" << std::endl
return 0;
}
画面に出るメッセージ(抜粋)
error: expected ‘;’ before ‘return’
何が起きているか:文の終わりにある ; が抜けているため、次の行の return までを1つの文として読もうとして止まっています。
直し方:メッセージが指している行の1つ前の行末に ; を付けます。
コンパイル時 宣言されていない名前(std:: の付け忘れ)
エラーになるコード
#include <iostream>
#include <string>
int main() {
string name = "C++";
std::cout << name << std::endl;
return 0;
}
画面に出るメッセージ(抜粋)
error: ‘string’ was not declared in this scope
何が起きているか:標準ライブラリの string は std という名前空間の中にあるため、std:: を付けないと見つかりません。
直し方:std::string と書きます。#include の書き忘れでも同じメッセージが出るので、必要なヘッダーが入っているかも確かめます。
実行時 std::out_of_range(範囲外の at())
エラーになるコード
#include <iostream>
#include <vector>
int main() {
std::vector<int> v = {10, 20, 30};
std::cout << v.at(3) << std::endl;
return 0;
}
画面に出るメッセージ(抜粋)
terminate called after throwing an instance of 'std::out_of_range'
何が起きているか:要素は3つなので使える番号は 0〜2 です。at() は範囲外の番号を渡すと std::out_of_range を投げ、誰も catch しないためプログラムが強制終了します。
直し方:番号が v.size() より小さいかを確かめてから取り出します。[] は範囲を調べないため、範囲外だと何が起きるか決まっていません。
コンパイル時 const の変数への代入
エラーになるコード
#include <iostream>
int main() {
const int max = 10;
max = 20;
std::cout << max << std::endl;
return 0;
}
画面に出るメッセージ(抜粋)
error: assignment of read-only variable ‘max’
何が起きているか:const を付けた変数は、最初に入れた値から変えられません。そこに代入しようとしたためコンパイルが止まっています。
直し方:値を変える必要があるなら const を外します。変えてはいけない値なら、別の変数を用意します。
このツールについて
C言語にクラスやテンプレートを加えた言語で、速さが求められるソフトウェアで長く使われています。
C++ の概要と、同じ5つのお題・出力当てクイズ・よくあるエラー。出力は g++ で確かめたもの。
主な機能
- 概要(誕生・作った人・型・動かし方)と日本での使われ方・新人研修・資格
- 5つのお題のコード例(C・Java・C#・COBOL・VBA の同じお題へ移れる)
- 出力当てクイズ4問(正解と理由)
- よくあるエラー4件(g++ の実際のメッセージ・原因・直し方)
- 出力は g++ 13.3.0 で照合済み・送信ゼロ
よくある質問
Q. このページで C++ のコードを実行できますか?
A. できません。ブラウザの中で動かせて、送信ゼロの方針に合う C++ の処理系が見つからなかったため、解説だけのページにしています。表示している出力とエラーのメッセージは、g++ 13.3.0 で実際にコンパイル・実行して確かめたものです。
Q. C言語と C++ はどう違いますか?
A. C++ は C言語にクラス・テンプレート・例外・標準ライブラリ(std::string や std::vector など)を加えた言語で、C のコードの多くはそのまま C++ としても通ります。このページと C言語のページは同じ5つのお題を並べているので、書き方の違いを見比べられます。
Q. エラーのメッセージの引用符が ‘ ’ になっているのはなぜですか?
A. g++ は UTF-8 の環境では引用符に ‘ ’ を使うためです。ロケールが C の環境では ' ' と表示されます。
入力値はURLの「#」以降に入るためサーバーには送信されません。リンクを開くと同じ状態を復元します。