C++ 基礎編 1日目

C++とは、C言語の機能を拡張した言語で、C言語とは異なる言語です
簡単にC言語との違いやコンパイルの方法など書いた以下のページをご参照ください
参考:C言語とC++の違い

C++入門

C++で文字列を標準出力するプログラムから、C++の書き方を見ていきましょう

#include <iostream> // std::coutの使用のため

/*
Hello Worldと出力
*/
int main(){
    // コンソールにHello Worldと出力
    std::cout << "Hello World" << std::endl;

    // 正常に終了
    return 0;
}
実行結果
Hello World

ヘッダファイル

まず1行目です

#include <iostream>

これは、標準で提供されている入出力機能を有効にするための様々な機能がつまった<iostream>というヘッダを読み込むという命令になります
C++の標準ライブラリは、C言語の標準ライブラリを包含しつつ、C++固有の機能に対応した新しいヘッダファイルを提供する必要があったため、拡張子.hがつかないファイルが用意されました
参考:iostream – cpprefjp C++日本語リファレンス

main()関数

C言語と同様にmain()という名前の関数は、一番はじめに実行される特別な関数です

起動時にコマンドライン・パラメータを渡す場合はC言語同様に、以下のように記述します

#include <iostream> 

/*
コマンドパラメータを取得し表示する
*/
int main(int argc,char* argv[]){
    // コンソールに引数を表示する
    for (int i=0;i<argc;i++){
        std::cout << "argv[" << i << "]=" << argv[i] << std::endl;
    }

    // 正常に終了
    return 0;
}
admin@lenovo:~/work$ ./a.out 1 2 3
argv[0]=./a.out
argv[1]=1
argv[2]=2
argv[3]=3

また、main()関数を抜ける場合正常に終了したことを表すため、

return 0;

を記述しています

コメント

/*
Hello Worldと出力
*/
int main(){
    // コンソールにHello Worldと出力

コメントは//または/* */の2通り書く方法があります

標準出力

以下の行についての説明をします

std::cout << "Hello World" << std::endl;
  1. std::cout
    std はC++の標準ライブラリやC言語互換ライブラリの要素を修飾するために使用される名前空間(ネームスペース)です

cout は std 名前空間の中にあるオブジェクトで、標準出力ストリーム (standard output stream) を表します
通常、標準出力はコンソールの画面に接続されています
詳細はストリーム をご覧ください

<< は挿入演算子と呼ばれます
この演算子は、右側のデータを左側のストリームに「挿入」する役割を果たします
ここでは、文字列 “Hello World” を標準出力ストリーム std::cout に送る(表示する)という意味になります

挿入演算子<< は、C++において左シフト演算子(<<)の意味も持っています

整数型に対する <<:
整数型(int、unsigned int など)のオペランドに対して使用された場合、<< はビット単位の左シフト演算を行います
これは、左側のオペランドのビットを指定された数だけ左に移動させ、空いたビットには0を詰める操作です

ストリーム型に対する <<:
std::ostream クラス(およびその派生クラスである std::cout など)のオブジェクトが左側のオペランドである場合、<< は挿入演算子として機能します
右側のオペランド(様々なデータ型が可能)の内容をストリームに書き出す役割を果たします
以下は、ストリームと整数に対して演算子を使用したサンプルプログラムです

std::cout << "0x001 << 4 = " << (0x001 << 4) << std::endl;
実行結果
0x001 << 4 = 16
(0x001 << 4)

(0x001 << 4) の部分では、整数リテラル 0x001 (16進数の 1、2進数では 00000001) に対して、左シフト演算子 << が適用されています
4 ビット左にシフトすると、00010000 (16進数では 0x010、10進数では 16) になります

  1. “Hello World”
    これは文字列リテラル (string literal) と呼ばれるもので、二重引用符 (“) で囲まれた一連の文字です
    このプログラムでは、表示したいメッセージそのものです
  2. << std::endl

再び挿入演算子 << が使われています
std::endl も std 名前空間にあるオブジェクトで、改行文字を出力し、さらに出力バッファをフラッシュ(強制的に出力)する役割を持ちます
改行文字を出力することで、カーソルが次の行の先頭に移動します
出力バッファのフラッシュは、プログラムが意図したタイミングで確実に出力が行われるようにするためのものです
std::endlマニピュレータの一つです

名前空間(ネームスペース)

名前空間とは、C++で名前の衝突を避けるために導入された機能です

名前の衝突とは、同じ名前の変数や関数が複数定義されてしまうことで、どれを参照すべきか分からなくなることです

名前空間を用いた例

まず、以下のソースを実行してみてください

#include <iostream>

// 自分で作成したremove関数
void remove(int value) {
  std::cout << "自作のremove関数が呼ばれました: " << value << std::endl;
}

namespace MyLibrary {
  // MyLibraryネームスペース内のremove関数
  void remove(double value) {
    std::cout << "MyLibraryのremove関数が呼ばれました: " << value << std::endl;
  }

  int data = 10;
}

int main() {
  // グローバルスコープのremove関数を呼び出す
  remove(5);

  // MyLibraryネームスペース内のremove関数を呼び出す
  MyLibrary::remove(3.14);

  // MyLibraryネームスペース内のdata変数にアクセスする
  std::cout << "MyLibraryのdata: " << MyLibrary::data << std::endl;

  return 0;
}
  // グローバルスコープのremove関数を呼び出す
  remove(5);

名前空間を指定せずに関数を呼び出した場合、コンパイラは現在のスコープ(この場合はグローバルスコープ)から順に同じ名前の関数を探します

名前探索の順序:
コンパイラが remove(5) という呼び出しを処理する際、まず現在のスコープ(main関数内)を探します
もし見つからなければ、その外側のスコープ(この場合はグローバルスコープ)を探します
グローバルスコープで void remove(int value) という関数が見つかったため、それが呼び出されました

次に以下のソースをコンパイルしてみてください

#include <iostream>

void remove(int value) {
  std::cout << "グローバルスコープのremove (int): " << value << std::endl;
}
void remove(double value) {
   std::cout << "別のグローバルスコープのremove (double): " << value << std::endl;
}

namespace MyLibrary {
  void remove(double value) {
    std::cout << "MyLibraryのremove (double): " << value << std::endl;
  }
}


int main() {
  remove(5); // これはグローバルスコープのremove(int)を呼び出す

  MyLibrary::remove(3.14); // これはMyLibraryのremove(double)を呼び出す

  // usingディレクティブを使うと名前の衝突が起こる可能性
  using namespace MyLibrary;
  remove(10.5); // エラー!グローバルスコープのremove(int)とMyLibraryのremove(double)のどちらを呼ぶべきか曖昧

  return 0;
}

グローバルスコープにremove(int)とremove(double)を定義しています  

以下のようなコンパイルエラーが発生します

remove.cpp:24:9: error: call of overloaded ‘remove(double)’ is ambiguous
   24 |   remove(10.5); // エラー!グローバルスコープのremove(int)とMyLibraryのremove(double)のどちらを呼ぶべきか曖昧
      |   ~~~~~~^~~~~~

using namespace MyLibraryを使うと、MyLibrary 名前空間内のすべての名前が現在のスコープ(main関数内)に持ち込まれます
この状態で remove(10); を呼び出すと、コンパイラはグローバルスコープの remove(int) と MyLibrary 名前空間から持ち込まれた remove(double) のどちらを呼び出すべきか判断できず、曖昧な呼び出しであるとしてコンパイルエラーが発生します

基本的に変数や関数、クラス名は重複しないようにしなければなりません
しかし、大規模なプログラムではどんな名前が使われているか把握するのは難しいです
そこで名前空間を使います
名前空間は、名前の衝突を防ぎ、大規模なプロジェクトや複数のライブラリを組み合わせる際に、名前の管理を容易にするための重要な仕組みなのです

名前空間の定義方法

また、名前空間は自分で定義することもできます
以下は、名前空間を作成したサンプルプログラムです

#include <iostream>

// myという名前空間を定義
namespace my {
  // my::helloという関数を定義
  void hello() {
    std::cout << “Hello, world!\n”;
  }
}

int main() {
  // my::helloを呼び出す
  my::hello();
  return 0;
}

名前空間を入れ子にして定義することもできます

#include <iostream>

// outerという名前空間を定義
namespace outer {
  // outer::innerという名前空間を定義
  namespace inner {
    // outer::inner::fooという関数を定義
    void foo() {
      std::cout << “Foo!\n”;
    }
  }
}

int main() {
  // outer::inner::fooを呼び出す
  outer::inner::foo();
  return 0;
}

ストリーム

ストリームとは、プログラムと入出力先(ファイル、ネットワーク、コンソールなど)との間でデータをやり取りする流れを抽象化した概念です
ストリームを利用することで、異なる入出力先(ファイルやネットワークやコンソール)に対して統一的な方法でデータの読み書きを行うことができます

C++では、<iostream>, <fstream>, <sstream> などのストリームライブラリを通じて、様々な入出力先に対応したストリームオブジェクトを利用できます
これらのストリームオブジェクトは、データの流れを抽象化したものです

標準ストリーム

プログラム起動時に自動的に用意されるストリームです

std::cin: 標準入力ストリーム (通常はキーボードからの入力)
std::cout: 標準出力ストリーム (通常はコンソールへの出力)
std::cerr: 標準エラーストリーム (通常はコンソールへのエラー出力)
std::clog: 標準ロギングストリーム (通常はコンソールへのログ出力)

std は「標準 (standard)」という意味の名前空間 (namespace) です
参考:std::basic_ostream – cppreference.com

ファイルストリーム

ファイルに対する読み書きを行うためのストリームです

std::ifstream: ファイルからの入力ストリーム
std::ofstream: ファイルへの出力ストリーム
std::fstream: ファイルに対する入出力両方の機能を持つストリーム

文字列ストリーム

メモリ上の文字列に対する読み書きを行うためのストリームです

std::istringstream: 文字列からの入力ストリーム
std::ostringstream: 文字列への出力ストリーム
std::stringstream: 文字列に対する入出力両方の機能を持つストリーム

ストリームの操作

ストリームに対して行う主な操作は、データの挿入 (出力) と抽出 (入力) です

出力

<< 演算子 (挿入演算子) を用いて、データをストリームに書き込みます

std::cout << "Hello" << " " << 123 << std::endl;
入力

>> 演算子 (抽出演算子) を用いて、ストリームからデータを読み込みます

int num;
std::cin >> num;

>>,<<の演算子は、様々なデータ型に対してオーバーロードされており、型に応じた適切なデータのやり取りを自動的に行ってくれます
※ただし、<< および >> 演算子はテキストファイルの入出力に適しています
バイナリファイルの読み書きの場合は、read、write関数を利用します

マニピュレータ

マニピュレータ(manipulator)は、C++の ヘッダーで定義されている特殊な関数またはオブジェクトであり、ストリーム(入力 std::cin や出力 std::cout など)のフォーマットや動作を制御するために使用されます

マニピュレータは、ストリームへの挿入演算子 (<<) や抽出演算子 (>>) と組み合わせて使用され、あたかもデータを出力または入力するかのように記述できます

たとえば、整数を16進で表示するには以下のように記述します

std::cout << std::hex << 255 << std::endl;
実行結果
ff

std::hexをストリームに挿入すると、それ以降に出力される整数値が16進形式で表示されるようになります

マニピュレータの一覧については入出力マニピュレータ – cppreference.comをご覧ください

コメント

この記事へのコメントはありません。

関連記事

Python 基本編 1日目

C++ 4日目:電卓アプリの仕様の確認

Python 基本編 4日目

PAGE TOP