コンテンツにスキップ

第 10 回 | 2024-12-06

  • 真偽を表す型を学ぶ
  • 否定の記号を学ぶ
  • const について学ぶ
  • 数学関数を学ぶ
  • 関数の作り方を学ぶ
  • 関数を作る練習をする
  • Siv3D をインストールする方法を学ぶ

1. 真偽を表す型 bool

プログラムの中で、Yes か No の二択の状態を表現するとき、例えば int 型の 01 で表現することができるが、どちらが Yes なのか、2-1 はどうなのかといった曖昧さが生じる。C++ では、このような二択の状態を表現するための専用の型 bool が用意されている。

bool 型は、truefalse の二つの値だけを持つ。true は Yes(真)を表し、false は No(偽)を表す。

1.1 bool 型の値

  • bool 型の値は if 文の条件式に使うことができる。
  • true は真を表し、その if 文の中の処理が実行される。
  • false は偽を表し、その if 文の中の処理は実行されない。else ブロックがあれば、else ブロックの中の処理が実行される。

#include <iostream>

int main()
{
	bool hasEnoughMoney = true;

	if (hasEnoughMoney)
	{
		std::cout << "買う!\n";
	}
	else
	{
		std::cout << "買わない。\n";
	}
}
出力
買う!

1.2 比較演算の結果は bool 型

  • <== などの比較演算子の結果は bool 型である。

#include <iostream>

int main()
{
	int money = 1000;
	int price = 2000;
	bool hasEnoughMoney = (price <= money);

	if (hasEnoughMoney)
	{
		std::cout << "買う!\n";
	}
	else
	{
		std::cout << "買わない。\n";
	}
}
出力
買わない。

1.3 && と ||

  • bool 型の値を &&|| でつなげることができる。
  • 次のコードでは、所持金が十分かつ割引がある場合に商品を買う。

#include <iostream>

int main()
{
	int money = 5000;
	int price = 2000;
	bool hasEnoughMoney = (price <= money);
	bool isDiscounted = false;

	if (hasEnoughMoney && isDiscounted)
	{
		std::cout << "買う!\n";
	}
	else
	{
		std::cout << "買わない。\n";
	}
}
出力
買わない。

2. 否定の記号 !

2.1 否定の記号 ! の使い方

  • ! は否定を表す記号で、bool 型の値を反転させた結果を返す。

#include <iostream>

int main()
{
	bool isDiscounted = false;

	if (!isDiscounted)
	{
		std::cout << "割引なし\n";
	}
	else
	{
		std::cout << "割引あり\n";
	}
}
出力
割引なし

2.2 複雑な式の中での ! の使用

#include <iostream>

int main()
{
	int money = 5000;
	int price = 2000;
	bool isDiscounted = false;

	if ((price <= money) && !isDiscounted)
	{
		std::cout << "所持金は十分だが、割引がないので買わない。\n";
	}
}
title
所持金は十分だが、割引がないので買わない。

3. 再代入を禁止する const

3.1 const の使い方

  • 値を変更する予定が無い、あるいは変更させたくない変数を作る場合、型名の前に const を付ける。
  • const の付いた変数は、それ以降変更できなくなる(変更操作がコンパイルエラーになる)。
  • const によって、プログラマのミスによる意図しない変数の値の変更を防ぐことができるため、使える場面では積極的に const を使うようにする。
  • 一般に、const を使っていれば使っているほど、良く設計された C++ コードと言える。
  • 次のコードでは、商品の値段を固定するため const を付けている。商品の値段を途中で変更しようとするとコンパイルエラーになる。
#include <iostream>

int main()
{
	// 商品の値段
	const int price = 300;

	// 買う個数
	int n;
	std::cin >> n;

	//price = 3000; // コンパイルエラー: const を付けた変数は値を変更できない

	std::cout << (price * n) << '\n';
}

4. 数学関数の使用

4.1 絶対値

  • <cmath> をインクルードすると使える std::abs(x) 関数は、渡した double 型の値 x の絶対値を double 型で返す。
  • abs は absolute value(絶対値)の略。

#include <iostream>
#include <cmath>

int main()
{
	const double x = std::abs(6.5);

	std::cout << x << '\n';

	std::cout << std::abs(-3.5) << '\n';

	std::cout << std::abs(0.0) << '\n';

	const double y = -2.3;

	std::cout << std::abs(y) << '\n';
}
出力
6.5
3.5
0
2.3

4.2 四捨五入

  • <cmath> をインクルードすると使える std::round(x) 関数は、渡した double 型の値 x を四捨五入した値を double 型で返す。

#include <iostream>
#include <cmath>

int main()
{
	const double x = std::round(1.4);

	std::cout << x << '\n';

	std::cout << std::round(1.5) << '\n';

	std::cout << std::round(-3.3) << '\n';

	const double y = 55.5;

	std::cout << std::round(y) << '\n';
}
出力
1
2
-3
56

4.3 平方根

  • <cmath> をインクルードすると使える std::sqrt(x) 関数は、渡した double 型の値 x の平方根を double 型で返す。
  • sqrt は square root(平方根)の略。

#include <iostream>
#include <cmath>

int main()
{
	const double x = std::sqrt(2.0);

	std::cout << x << '\n';

	std::cout << std::sqrt(16.0) << '\n';

	std::cout << std::sqrt(1.21) << '\n';

	const double y = 55.5;

	std::cout << std::sqrt(y) << '\n';
}
出力
1.41421
4
1.1
7.44983

4.4 最小値

  • <algorithm> をインクルードすると使える std::min(a, b) 関数は、渡した値 a, b の小さいほうを返す。

#include <iostream>
#include <algorithm>

int main()
{
	const int x = std::min(10, 20);

	std::cout << x << '\n';

	std::cout << std::min(-20, 40) << '\n';

	std::cout << std::min(40, -20) << '\n';

	const int y = 15;

	std::cout << std::min(x, y) << '\n';
}
出力
10
-20
-20
10

4.5 最大値

  • <algorithm> をインクルードすると使える std::max(a, b) 関数は、渡した値 a, b の大きいほうを返す。

#include <iostream>
#include <algorithm>

int main()
{
	const int x = std::max(10, 20);

	std::cout << x << '\n';

	std::cout << std::max(-20, 40) << '\n';

	std::cout << std::max(40, -20) << '\n';

	const int y = 15;

	std::cout << std::max(x, y) << '\n';
}
出力
20
40
40
20

4.6 min と max の応用

  • std::min(a, b) 関数や std::max(a, b) 関数は double 型にも対応する。
  • a, b は両方とも同じ型にする必要がある。
    • 例: abint
    • 例: abdouble

#include <iostream>
#include <algorithm>

int main()
{
	std::cout << std::min(5.5, 3.3) << '\n';

	std::cout << std::max(5.5, 3.3) << '\n';
}
出力
3.3
5.5

5. 関数の作り方

5.1 関数を作る

  • 仕事に名前を付けることができ、これを関数という。
  • 関数は「関数名」と「関数呼び出しの記号 ()」を使って呼び出す(実行する)ことができる。
  • 関数を作る最も基本の文法は次の通りである。
void 関数名()
{
	仕事
}
  • 使用例:

#include <iostream>

// Hello! と出力する仕事をする関数
void ShowMessage()
{
	std::cout << "Hello!\n";
}

int main()
{
	ShowMessage();
	ShowMessage();
}
出力
Hello!
Hello!

5.2 関数に引数を渡す

  • 関数に特定の型の値をコピーして渡し、仕事の中で使ってもらうことができる。
  • 渡す値 / 渡された値のことを引数(ひきすう)といい、前者を実引数、後者を仮引数と区別する。
  • 引数がある関数を作る文法は次のとおりである。
void 関数名(引数の型 仮引数の変数名)
{
	仕事
}
  • 使用例:

#include <iostream>

void ShowMessage()
{
	std::cout << "Hello!\n";
}

void ShowInt(int n)
{
	std::cout << n << '\n';
}

void ShowDouble(double x)
{
	std::cout << x << '\n';
}

int main()
{
	ShowMessage();
	ShowMessage();

	ShowInt(123);
	ShowInt(-2000);

	ShowDouble(1.23);
	ShowDouble(-55.5);
}
出力
Hello!
Hello!
123
-2000
1.23
-55.5

  • 仮引数の変数名は任意であり、仮引数の変数のスコープはその関数内のみである。
  • プログラムの中に関数が複数あっても、プログラムが最初に main() 関数を実行してくれるのは、C++ では main() 関数を最初に実行すると決められているためである。このような最初に呼び出される関数のことをエントリーポイントという。

5.3 複数の引数を渡す関数を作る

  • 複数の引数を渡す関数を作る文法は次のとおりである。
void 関数名(第一引数の型 第一引数の変数名, 第二引数の型 第二引数の変数名)
{
	仕事
}
  • 使用例:

#include <iostream>

// 指定した回数だけ指定した文字を出力し、最後に改行する
void ShowLine(int count, char ch)
{
	for (int i = 0; i < count; ++i)
	{
		std::cout << ch;
	}

	std::cout << '\n';
}

int main()
{
	ShowLine(10, '*');
	
	ShowLine(5, '-');
	
	ShowLine(10, 'w');
}
出力
**********
-----
wwwwwwwwww

  • 3 つ以上の引数についても同様である。
void 関数名(第一引数の型 第一引数の変数名, 第二引数の型 第二引数の変数名, 第三引数の型 第三引数の変数名)
{
	仕事
}

同じ型の引数を、型を省略してまとめて記述することはできない

正しくないコード(コンパイルエラー)
void Function(int a, b, c)
{

}
正しいコード
void Function(int a, int b, int c)
{

}

5.4 戻り値を返す関数を作る (1)

  • 関数は、仕事の結果を、呼び出した側に return 結果; によって返すことができる。
  • 返す値 / 返された値のことを戻り値(もどりち)という。
  • 関数は return すると、そこで仕事を終了する。
  • 戻り値がある関数を作る文法は次のとおりである。
戻り値の型 関数名(引数の型 仮引数の変数名)
{
	仕事

	return 戻り値;
}
  • 使用例:

#include <iostream>

// 与えられた整数の平方を返す
int Square(int n)
{
	return (n * n);
}

int main()
{
	int a = Square(5);

	std::cout << a << '\n';

	std::cout << Square(11) << '\n';

	 // 関数の戻り値を引数にして関数を呼ぶこともできる
	std::cout << Square(Square(4)) << '\n';
}
出力
25
121
256

  • 5.4 のように 1 行で戻り値を返す関数もあれば、複数行の仕事をしてから戻り値を返す関数もある。

5.5 戻り値を返す関数を作る (2)

  • return を複数書くような関数もある。

#include <iostream>

// 与えられた 2 つの整数の差の絶対値を返す
int AbsDiff(int a, int b)
{
	if (a < b)
	{
		return (b - a);
	}
	else
	{
		return (a - b);
	}
}

int main()
{
	std::cout << AbsDiff(50, 80) << '\n';

	std::cout << AbsDiff(1, 11) << '\n';
}
出力
30
10

5.6 戻り値が void である関数の return

  • 戻り値が void である関数(戻り値の無い関数)は値を返せないが、return; によって関数をそこで終了できる。

#include <iostream>

void ShowFee(int age)
{
	if (age <= 3)
	{
		std::cout << "無料\n";
		return;
	}

	std::cout << "2000 円\n";
}

int main()
{
	ShowFee(40);
	ShowFee(10);
	ShowFee(3);
}
出力
2000 円
2000 円
無料

6. 関数を作る練習

6.1 与えられた 2 つの整数のうち、大きい方の値を返す関数

  • std::max() のような関数を自分で作ると次のようになる。

#include <iostream>

// 与えられた 2 つの整数のうち、大きい方の値を返す関数
int MaxInt(int a, int b)
{
	if (a < b)
	{
		return b;
	}
	else
	{
		return a;
	}
}

int main()
{
	std::cout << MaxInt(80, 50) << '\n';
	std::cout << MaxInt(-100, -50) << '\n';
}
出力
80
-50

6.2 与えられた 4 つの整数のうち、最大の値を返す関数

  • 複雑な関数を作るとき、既存の関数を部品として使うことで、簡単に書けることがある。

#include <iostream>

// 与えられた 2 つの整数のうち、大きい方の値を返す関数
int MaxInt(int a, int b)
{
	if (a < b)
	{
		return b;
	}
	else
	{
		return a;
	}
}

// 与えられた 4 つの整数のうち、最大の値を返す関数
int MaxInt4(int a, int b, int c, int d)
{
	return MaxInt(MaxInt(a, b), MaxInt(c, d));
}

int main()
{
	std::cout << MaxInt4(80, 50, 20, 0) << '\n';
	std::cout << MaxInt4(-100, -50, -10, -200) << '\n';
	std::cout << MaxInt4(0, 999, 0, 0) << '\n';
}
出力
80
-10
999

6.3 二点間の距離を求める関数

#include <iostream>
#include <cmath>

// 二点 (x1, y1), (x2, y2) 間の距離を返す
double Distance(double x1, double y1, double x2, double y2)
{
	return std::sqrt((x2 - x1) * (x2 - x1) + (y2 - y1) * (y2 - y1));
}

int main()
{
	std::cout << Distance(0.0, 0.0, 10.0, 10.0) << '\n';
	std::cout << Distance(3.0, 2.0, 7.0, 5.0) << '\n';
}
出力
14.1421
5

<cmath>std::hypot() を使う

<cmath>std::hypot(x, y) 関数は、直角三角形の直角をなす辺の長さがそれぞれ x, y のときの斜辺の長さを返す。これを使うこともできる。

#include <iostream>
#include <cmath>

// 与えられた二次元座標間の距離を返す関数
double Distance(double x1, double y1, double x2, double y2)
{
	return std::hypot((x2 - x1), (y2 - y1));
}

int main()
{
	std::cout << Distance(0.0, 0.0, 10.0, 10.0) << '\n';
	std::cout << Distance(3.0, 2.0, 7.0, 5.0) << '\n';
}
出力
14.1421
5

7. Siv3D(シブスリーディー)プログラミングの準備

  • 第 12 回以降の講義の「グラフィックスプログラミング」では、C++ コードでゲームやアプリを簡単に開発できるフレームワーク「Siv3D」を使用する。

7.1 C++ の開発環境をインストールする

7.2 Siv3D をインストールする

順番を守る

  • 7.1 が完了するまでは、ここの内容は実行しない。
  • Windows で 7.1 が完了したら、一度 Visual Studio を終了してから、この内容を実行する。
  • C++ コードでゲームやアプリを簡単に開発できるフレームワーク「Siv3D」をインストールする。Siv3D 公式サイト にアクセスし、「Siv3D をダウンロードする」から、使用している OS に応じた開発キットをダウンロードする。
  • 該当ページの指示通り進めれば、Siv3D で作成したプログラムの実行までたどり着ける。
  • 次のような画面が表示されたら、インストールは成功。

トラブルシューティング

① Windows の 3. の手順でどれだけ探しても「OpenSiv3D_0.6.15」の項目が見つからない

  • 手順ページにある『プロジェクト テンプレートの項目に「OpenSiv3D」が見つからない場合』を参照してください。

② Windows の 4. の手順でエラーが出て実行できない

  • Visual Studio 2022 が最新のバージョン(17.12 以降)になっているかを確認し、Visual Studio Installer を使って Visual Studio 2022 の更新を行ってください。

③ M1 ~ M3 mac の 3. の手順でエラーが発生し、プログラムが起動しない。

  • 手順ページにある『M1 - M3 Mac における Rosetta モードの有効化』を参照してください。

④ それ以外

  • Moodle または講義時間内に質問してください。Moodle での質問時は、トラブルの様子を示すスクリーンショットを添えてください。

✅ 振り返りチェックリスト

  • 真偽を表す型を学んだ
  • 否定の記号を学んだ
  • const について学んだ
  • 数学関数を学んだ
  • 関数の作り方を学んだ
  • 関数を作る練習をした
  • Siv3D をインストールする方法を学んだ

📝 課題

🐣 練習問題

標準出力を使って図形を描く関数を実装してください。解答テンプレートが与えられるので、それを完成させてください。実装する関数は次の 1 つです。

1. 長方形を描く関数

void DrawRectangle(int width, int height, char ch);
引数 説明
width 長方形の幅
height 長方形の高さ
ch 描画に使う文字

解答テンプレート
#include <iostream>

void DrawRectangle(int width, int height, char ch)
{
	// ここにコードを書く
}

int main()
{
	DrawRectangle(5, 1, '*');

	std::cout << '\n';

	DrawRectangle(3, 6, '@');

	std::cout << '\n';

	DrawRectangle(10, 5, '#');
}
出力
*****

@@@
@@@
@@@
@@@
@@@
@@@

##########
##########
##########
##########
##########

解答例
#include <iostream>

void DrawRectangle(int width, int height, char ch)
{
	for (int y = 0; y < height; ++y)
	{
		for (int x = 0; x < width; ++x)
		{
			std::cout << ch;
		}

		std::cout << '\n';
	}
}

int main()
{
	DrawRectangle(5, 1, '*');

	std::cout << '\n';

	DrawRectangle(3, 6, '@');

	std::cout << '\n';

	DrawRectangle(10, 5, '#');
}

📝 ① 提出課題(50 点)

標準出力を使って図形を描く関数を実装してください。解答テンプレートが与えられるので、それを完成させてください。

実装する関数は次の 3 つです。

1. 長方形の枠を描く関数

void DrawRectangleOutline(int width, int height, char ch);
引数 説明
width 長方形の幅
height 長方形の高さ
ch 枠の描画に使う文字
  • 長方形の枠以外の部分は空白文字を使う。

2. チェッカーボードを描く関数

void DrawCheckerboard(int width, int height, int cellSize, char ch1, char ch2);
引数 説明
width チェッカーボード全体の幅
height チェッカーボード全体の高さ
cellSize チェッカーボードの 1 マスの幅および高さ
ch1 チェッカーボードの最も左上のマスの描画に使う文字
ch2 もう一方のマスの描画に使う文字

3. 円を描く関数

void DrawCircle(int radius);
引数 説明
radius 円の半径
  • 描画には塗りつぶし文字として # を使い、円以外の部分は空白文字を使います。
  • 【ボーナス加点】円の周において、必要な部分に中間色の塗りつぶし文字として + を使い、エッジをスムーズにします。専門用語でいうと「アンチエイリアス処理」を行ってください。外周のすべてを + で描くわけではないことに注意が必要です。

エッジをスムーズにする例

# だけで円のように見せるのは難しく、この状態では八角形のようにも見えます。

  ####  
 ###### 
########
########
########
########
 ###### 
  ####  

周の一部を中間色 + で表現することで、エッジがスムーズになり、円に近い見た目になります。

  +##+  
 ###### 
+######+
########
########
+######+
 ###### 
  +##+  

解答テンプレート
#include <iostream>
#include <cmath>

void DrawRectangleOutline(int width, int height, char ch)
{
	// ここにコードを書く
}

void DrawCheckerboard(int width, int height, int cellSize, char ch1, char ch2)
{
	// ここにコードを書く
}

void DrawCircle(int radius)
{
	// ここにコードを書く
}

int main()
{
	DrawRectangleOutline(10, 5, '*');
	std::cout << '\n';
	DrawRectangleOutline(3, 3, '#');
	std::cout << '\n';
	DrawRectangleOutline(1, 5, '@');
	std::cout << '\n';
	
	DrawCheckerboard(10, 8, 2, 'O', '#');
	std::cout << '\n';
	DrawCheckerboard(12, 10, 3, ' ', '#');
	std::cout << '\n';
	DrawCheckerboard(15, 15, 4, '-', '|');
	std::cout << '\n';

	DrawCircle(4);
	std::cout << '\n';
	DrawCircle(6);
	std::cout << '\n';
	DrawCircle(12);
	std::cout << '\n';
}
出力例
**********
*        *
*        *
*        *
**********

###
# #
###

@
@
@
@
@

OO##OO##OO
OO##OO##OO
##OO##OO##
##OO##OO##
OO##OO##OO
OO##OO##OO
##OO##OO##
##OO##OO##

   ###   ###
   ###   ###
   ###   ###
###   ###   
###   ###   
###   ###   
   ###   ###
   ###   ###
   ###   ###
###   ###   

----||||----|||
----||||----|||
----||||----|||
----||||----|||
||||----||||---
||||----||||---
||||----||||---
||||----||||---
----||||----|||
----||||----|||
----||||----|||
----||||----|||
||||----||||---
||||----||||---
||||----||||---

  +##+  
 ###### 
+######+
########
########
+######+
 ###### 
  +##+  

   +####+   
  ########  
 ########## 
+##########+
############
############
############
############
+##########+
 ########## 
  ########  
   +####+   

		++####++        
	  +##########+      
	+##############+    
   +################+   
  +##################+  
  ####################  
 +####################+ 
 ###################### 
+######################+
+######################+
########################
########################
########################
########################
+######################+
+######################+
 ###################### 
 +####################+ 
  ####################  
  +##################+  
   +################+   
	+##############+    
	  +##########+      
		++####++        

  • DrawCircle の表示結果は、計算方法によって異なる場合があります。若干の違いは許容されます。
  • 採点時には main() 関数にテストケースを追加します。解答テンプレート以外の文字やサイズにも対応できるようにしてください。

課題 ① は、コードまたはコードの共有 URL(Wandbox)を提出してください。

円の描画のヒント
  • 一辺が半径の倍の長さを持つ正方形を考えます。
  • 各マス目の中心の座標について、円の中心の座標からの距離を考えます。

📝 ② 事前課題(50 点、提出なし)

  • 課題 ② で提出するものはありません。
  • 第 12 回以降の講義で必要になるため、7.1~7.3 を参考に C++ の開発ツールと Siv3D をインストールしてください。
  • 第 12 回の提出課題で一定の基準を満たした場合に、この課題で満点の 50 点を獲得します。それ以外の場合、この課題の得点は 0 点になります。

なるべく早めに取り組みましょう

インストールでトラブルがあった場合は、Moodle や講義時間でサポートします。第 12 回講義ぎりぎりになると講義時間内に対応できない可能性があり、今後の課題提出に支障が出るおそれがあります。