🖥️

【C++言語入門】 第8回 デフォルト引数

2025/02/27に公開

https://youtu.be/uWOk59apt08

四国めたん
\textcolor{pink}{四国めたん: }教師役ですわ

ずんだもん
\textcolor{lime}{ずんだもん: }生徒役なのだ

\footnotesize \textcolor{pink}{四国めたん:} こんにちは。四国めたんです

\footnotesize \textcolor{lime}{ずんだもん:} ずんだもんなのだ。こんにちはなのだ

\footnotesize \textcolor{pink}{四国めたん:} 今回は関数の デフォルト引数 についてお話ししますわ

\footnotesize \textcolor{lime}{ずんだもん:} 関数の引数のバリエーションか?

\footnotesize \textcolor{pink}{四国めたん:} あらかじめデフォルトの値が決まっている引数ですわね

\footnotesize \textcolor{lime}{ずんだもん:} 便利なのか?

\footnotesize \textcolor{pink}{四国めたん:} 関数を呼び出す際に引数の省略が可能なので、そこそこ便利ですわね

\footnotesize \textcolor{lime}{ずんだもん:} さっそく解説をお願いするのだ

引数にはデフォルトの値を設定できるのです

\footnotesize \textcolor{pink}{四国めたん:} 前前回は オーバーロード により同じ関数名で引数の異なる関数を定義可能なことをお話ししましたわ

\footnotesize \textcolor{lime}{ずんだもん:} おぼえているのだ

\footnotesize \textcolor{pink}{四国めたん:} 今回は 引数のデフォルト値 を指定することで、呼び出す際に引数を省略できる関数についてお話をしますわ

\footnotesize \textcolor{lime}{ずんだもん:} ふむふむ

\footnotesize \textcolor{pink}{四国めたん:} C言語では関数を呼び出す際はprintfなどの例外を除き、引数には必ず決められた型の値をセットする必要がありましたわ

\footnotesize \textcolor{lime}{ずんだもん:} たしかにその通りなのだ

\footnotesize \textcolor{pink}{四国めたん:} C++言語でも概ね状況は同じなのですが、関数を宣言する際に引数にデフォルトの値を指定することができるようになりましたわ

\footnotesize \textcolor{lime}{ずんだもん:} すごいのだ

\footnotesize \textcolor{pink}{四国めたん:} これにより、関数を呼び出す際に、よく使用される引数の値を指定する手間を省くことができるようになりましたわ

\footnotesize \textcolor{lime}{ずんだもん:} かなり便利になったのだ

\footnotesize \textcolor{pink}{四国めたん:} ただ、引数の数を少なくした関数を オーバーロード することでも同じことは可能ですわ

\footnotesize \textcolor{lime}{ずんだもん:} でも オーバーロード の手間が大変なのだ

\footnotesize \textcolor{pink}{四国めたん:} それでは 引数のデフォルト値 を指定する方法について見て行きましょう

\footnotesize \textcolor{lime}{ずんだもん:} お願いするのだ

\footnotesize \textcolor{pink}{四国めたん:} 基本は関数の宣言もしくは定義時に、引数名の後に= デフォルト値という形でデフォルト値を指定しますわ

\footnotesize \textcolor{lime}{ずんだもん:} 関数に宣言と定義の両方がある場合には、両方に指定するのか?

\footnotesize \textcolor{pink}{四国めたん:} いいえ、その場合には宣言側の引数にのみデフォルト値を指定しますわ

\footnotesize \textcolor{lime}{ずんだもん:} 両方に指定するとどうなるのだ?

\footnotesize \textcolor{pink}{四国めたん:} エラーとなりますわね

\footnotesize \textcolor{lime}{ずんだもん:} なるほどなのだ

\footnotesize \textcolor{pink}{四国めたん:} それでは前回のプログラム例で使用したCircleクラスのMessageメソッドに デフォルト引数 を追加してみましょう

circle.h
#pragma once

#ifndef CIRCLE_H
#define CIRCLE_H

#include <iostream>

const double kPI = 3.14159265358979323846;
const int kMessageSize = 50;

/// @brief 円
class Circle {
  double diameter_;  // 直径
  char* pmessage_;

 public:
  Circle(double diameter) : diameter_(diameter), pmessage_(nullptr) {
    pmessage_ = new char[kMessageSize];
  }
  ~Circle() {
    std::cout << "デストラクタが呼ばれました" << std::endl;
    delete[] pmessage_;
  }

  double Diameter() { return diameter_; }
  void Diameter(double diameter) { diameter_ = diameter; }

  double Area() {
    double radius = Diameter() / 2.0;
    double a = radius * radius * kPI;
    return a;
  }

  const char* Message(bool flag = false) {
    if (flag) {
      sprintf_s(pmessage_, kMessageSize, "円の直径は%fです。", Diameter());
    } else {
      sprintf_s(pmessage_, kMessageSize, "円の面積は%fです。", Area());
    }
    return pmessage_;
  }
};

#endif  // CIRCLE_H

\footnotesize \textcolor{pink}{四国めたん:} とりあえず"circle.cpp"は空にしておきますわ

\footnotesize \textcolor{lime}{ずんだもん:} 境界線の幅を消しているのだが...

\footnotesize \textcolor{pink}{四国めたん:} 当面は使いませんし、邪魔なので消しておきましたわ

\footnotesize \textcolor{lime}{ずんだもん:} なるほどなのだ

\footnotesize \textcolor{lime}{ずんだもん:} そしてMessageメソッドの引数にbool型のフラグを追加しているのだ

\footnotesize \textcolor{pink}{四国めたん:} はい、そして"flag"に= falseを追加することで、デフォルト値をfalseに指定していますわ

\footnotesize \textcolor{lime}{ずんだもん:} りょうかいなのだ

\footnotesize \textcolor{lime}{ずんだもん:} ところで"bool.h"をインクルードしていないが、問題ないのか?

\footnotesize \textcolor{pink}{四国めたん:} はい、C言語とは異なりC++言語ではbooltruefalseはキーワードとして追加されていますので、わざわざ"bool.h"をインクルードする必要はありませんわね

\footnotesize \textcolor{lime}{ずんだもん:} なるほどなのだ

\footnotesize \textcolor{pink}{四国めたん:} そしてflagの状態により、円の直径を示すメッセージか、円の面積を示すメッセージを返しますわ

\footnotesize \textcolor{lime}{ずんだもん:} ふむふむ

\footnotesize \textcolor{pink}{四国めたん:} ちなみに従来の引数のないMessageメソッドの呼び出しは、変更することなく同じ動作となりますわ

\footnotesize \textcolor{lime}{ずんだもん:} 引数としてfalseが指定されたとみなされるのだ

\footnotesize \textcolor{pink}{四国めたん:} はい、ほんの少しの気遣いですが、このような後方互換性は大切ですわ

\footnotesize \textcolor{lime}{ずんだもん:} たしかに...

\footnotesize \textcolor{pink}{四国めたん:} それではメイン関数側を変更して実行してみましょう

hello_world.cpp
#include <iostream>
#include "circle.h"

int main(int argc, char* argv[]) {
  Circle c(10.0);
  std::cout << c.Message(true) << std::endl;
  std::cout << c.Message() << std::endl;
  return 0;
}

デフォルト引数

\footnotesize \textcolor{lime}{ずんだもん:} 引数をtrueに指定したMessageメソッドでは直径を、引数を指定しない従来のMessageメソッドでは面積を表示しているのだ

定義と宣言を別にしてみよう

\footnotesize \textcolor{pink}{四国めたん:} ではMessageメソッドの定義を"circle.cpp"に移してみましょう

circle.h
#pragma once

#ifndef CIRCLE_H
#define CIRCLE_H

#include <iostream>

const double kPI = 3.14159265358979323846;
const int kMessageSize = 50;

/// @brief 円
class Circle {
  double diameter_;  // 直径
  char* pmessage_;

 public:
  Circle(double diameter) : diameter_(diameter), pmessage_(nullptr) {
    pmessage_ = new char[kMessageSize];
  }
  ~Circle() {
    std::cout << "デストラクタが呼ばれました" << std::endl;
    delete[] pmessage_;
  }

  double Diameter() { return diameter_; }
  void Diameter(double diameter) { diameter_ = diameter; }

  double Area() {
    double radius = Diameter() / 2.0;
    double a = radius * radius * kPI;
    return a;
  }

  const char* Message(bool flag = false);
};

#endif  // CIRCLE_H
circle.cpp
#include "circle.h"

const char* Circle::Message(bool flag) {
  if (flag) {
    sprintf_s(pmessage_, kMessageSize, "円の直径は%fです。", Diameter());
  } else {
    sprintf_s(pmessage_, kMessageSize, "円の面積は%fです。", Area());
  }
  return pmessage_;
}

ソース中のデフォルト引数

\footnotesize \textcolor{lime}{ずんだもん:} 特に変わりはないのだ

\footnotesize \textcolor{pink}{四国めたん:} そうですわね

\footnotesize \textcolor{lime}{ずんだもん:} ところでMessageメソッドの宣言部分には= falseとしてデフォルトの値を指定しているが、定義側ではデフォルトの値を指定していないのだ

\footnotesize \textcolor{pink}{四国めたん:} はい、デフォルトの値は宣言側のみで指定するのが決まりですわ

\footnotesize \textcolor{lime}{ずんだもん:} 決まりならしかたがないのだ

\footnotesize \textcolor{pink}{四国めたん:} ちなみに定義側でconst char* Circle::Message(bool flag = false)とすると 基底引数の再定義 エラーとなりますわ

エラー

\footnotesize \textcolor{lime}{ずんだもん:} なるほどなのだ

デフォルトの引数の指定はいくつかの制約が...

\footnotesize \textcolor{lime}{ずんだもん:} ところで全ての引数に自由にデフォルトの値を指定できるのか?

\footnotesize \textcolor{pink}{四国めたん:} いいえ、引数にデフォルトの値を指定するには、いくつかの制約がありますわ

\footnotesize \textcolor{lime}{ずんだもん:} 詳しく教えて欲しいのだ

デフォルト値には定数を使用しましょう

\footnotesize \textcolor{pink}{四国めたん:} まず、引数に指定するデフォルトの値には基本的に定数を使用しますわ

\footnotesize \textcolor{lime}{ずんだもん:} そうなのか?

\footnotesize \textcolor{pink}{四国めたん:} はい、メンバ変数などは指定できても良さそうですが、C++言語では許されていませんわ

\footnotesize \textcolor{pink}{四国めたん:} 例えばCircleクラスのMessageの引数のデフォルトの値に"diameter_"を指定してもエラーとなるだけですわ

const char* Message(double d = diameter_);

エラー2

\footnotesize \textcolor{lime}{ずんだもん:} なるほどなのだ

デフォルトの引数は最後に...

\footnotesize \textcolor{pink}{四国めたん:} 次に、引数にデフォルト値を指定したら、後に続く引数にもデフォルト値を指定する必要がありますわ

\footnotesize \textcolor{lime}{ずんだもん:} つまりデフォルトの値を指定した引数は、後ろにまとめると云うことか...

\footnotesize \textcolor{pink}{四国めたん:} その通りですわ

型 関数名(型1 引数1, 型2 引数2, 型3 引数3 = 定数3, 型4 引数4 = 定数4);  // (1)
型 関数名(型1 引数1, 型2 引数2 = 定数2, 型3 引数3, 型4 引数4 = 定数4);  // (2)

\footnotesize \textcolor{pink}{四国めたん:} 例えば(1)のような関数はOKですが、(2)のような関数はNGですわね

\footnotesize \textcolor{lime}{ずんだもん:} どうしてなのだ?

\footnotesize \textcolor{pink}{四国めたん:} (2)では引数2にデフォルトの値が指定された後に、デフォルトの値が指定されていない引数3があるからですわ

\footnotesize \textcolor{lime}{ずんだもん:} なるほどなのだ

\footnotesize \textcolor{pink}{四国めたん:} まぁ、考えてみれば当然で、例えばこのようなコードがあったとしますわね

void function(int a, int b = 2, int c, int d = 4);

void main() {
  function(0, 1, 2);
}

\footnotesize \textcolor{lime}{ずんだもん:} ふむふむ

\footnotesize \textcolor{pink}{四国めたん:} さて、関数呼び出しでは引数"b"のデフォルト値が使われるのでしょうか?

\footnotesize \textcolor{pink}{四国めたん:} もしくは引数"d"のデフォルト値?

\footnotesize \textcolor{lime}{ずんだもん:} どちらとも判断がつかないのだ

\footnotesize \textcolor{pink}{四国めたん:} はい、結局、エラーとして処理するしかありませんわ

まとめ

\footnotesize \textcolor{pink}{四国めたん:} お疲れさまでした

\footnotesize \textcolor{lime}{ずんだもん:} おつかれさまなのだ

\footnotesize \textcolor{pink}{四国めたん:} 以上で デフォルト引数 の説明を終わりますわ

Discussion