🖥️

【C++言語入門】 第12回 参照

2025/03/13に公開

https://youtu.be/LI35zutq8zc

四国めたん
\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{pink}{四国めたん:} C言語では構造体を関数の引数にする場合には、ポインタとして渡すことが推奨されますわ

\footnotesize \textcolor{lime}{ずんだもん:} たしかに、関数の引数として構造体をそのまま渡してしまうと、構造体のコピーが渡されて大変なのだ

\footnotesize \textcolor{pink}{四国めたん:} はい、構造体がサイズの大きな配列などをメンバーとして持っていたりすると構造体自体のサイズも大きくなってしまいますわ

\footnotesize \textcolor{pink}{四国めたん:} そして引数としてコピーが渡されてしまうとオーバーヘッドが負担になってしまいますわね

\footnotesize \textcolor{lime}{ずんだもん:} C++言語でも事情は同じなのか?

\footnotesize \textcolor{pink}{四国めたん:} はい、クラスについても構造体と同じく、関数の引数としてはクラスのコピーでは無くポインタで渡すことが妥当かと思いますわ

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

\footnotesize \textcolor{pink}{四国めたん:} 例としてフルハイビジョンサイズの画像データを持つクラスを使って関数を呼んでみましょう

#include<iostream>

const int kWidth = 1920;
const int kHeight = 1080;

struct Color {
  unsigned char r;
  unsigned char g;
  unsigned char b;
  unsigned char a;
};

class Screen {
  Color scr_[kHeight][kWidth];
};

void Disp(Screen s) {
  std::cout << "スクリーンに表示" << std::endl;
  return;
}

int main(int argc, char* argv[]) {
  Screen* ps = new Screen();
  Disp(*ps);
  delete ps;
  ps = nullptr;
  return 0;
}

\footnotesize \textcolor{pink}{四国めたん:} クラスScreenにはメンバ変数としてRGBAの色のデータを1画面分の2次元配列として持たせていますわ

\footnotesize \textcolor{lime}{ずんだもん:} 画像のデータサイズは大きいのだ

\footnotesize \textcolor{pink}{四国めたん:} Disp関数内では特にScreenクラスのインスタンスを使って何かをしている訳ではありませんが、説明のためにとりあえず引数として渡していますわ

\footnotesize \textcolor{lime}{ずんだもん:} いわゆる 便宜上のため と云うやつなのだ

\footnotesize \textcolor{pink}{四国めたん:} そしてメイン関数内ではScreenクラスのインスタンスをnewで作成してDisp関数に渡していますわ

\footnotesize \textcolor{lime}{ずんだもん:} "ps"はScreenのインスタンスへのポインタなのだ

\footnotesize \textcolor{pink}{四国めたん:} はい、Disp関数の引数には間接演算子"*"を使ってScreenのインスタンスを渡していますわ

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

\footnotesize \textcolor{pink}{四国めたん:} Disp関数では渡されたScreenのインスタンスの コピー を引数として受け取り、処理をしますわ

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

\footnotesize \textcolor{pink}{四国めたん:} それでは実行してみましょう

クラスの引数

\footnotesize \textcolor{lime}{ずんだもん:} バンドルされていない例外 というエラーが発生しているのだ

\footnotesize \textcolor{pink}{四国めたん:} 内容は Stack overflow ですわね

\footnotesize \textcolor{lime}{ずんだもん:} どういうことなのだ?

\footnotesize \textcolor{pink}{四国めたん:} いわゆる スタックメモリ が足りない場合に起こるエラーですわ

メモリの警告

\footnotesize \textcolor{pink}{四国めたん:} 警告も出ていますし、引数でScreenのインスタンスをコピーする際にメモリが足りなくなったのだと思われますわ

\footnotesize \textcolor{lime}{ずんだもん:} どうにかならないのか?

\footnotesize \textcolor{pink}{四国めたん:} 引数をポインタ渡しに書き換えればOKですわ

#include<iostream>

const int kWidth = 1920;
const int kHeight = 1080;

struct Color {
  unsigned char r;
  unsigned char g;
  unsigned char b;
  unsigned char a;
};

class Screen {
  Color scr_[kHeight][kWidth];
};

void Disp(Screen* ps) {
  std::cout << "スクリーンに表示" << std::endl;
  return;
}

int main(int argc, char* argv[]) {
  Screen* ps = new Screen();
  Disp(ps);
  delete ps;
  ps = nullptr;
  return 0;
}

ポインタ使用

\footnotesize \textcolor{lime}{ずんだもん:} お~、問題がなくなったのだ

\footnotesize \textcolor{pink}{四国めたん:} 警告も消えていますわね

参照という技も使えます

\footnotesize \textcolor{pink}{四国めたん:} クラスや構造体を関数の引数として使う際にポインタで渡すことで、インスタンスのコピーを避け、メモリ領域やコピー時間を節約できますわ

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

\footnotesize \textcolor{pink}{四国めたん:} しかし ポインタとしてNULLが渡された場合にどうするのか とか、いろいろと面倒が付きまといますわ

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

\footnotesize \textcolor{pink}{四国めたん:} そこでC++言語では、 参照 もしくは reference という新しい仕組みを導入しましたわ

\footnotesize \textcolor{lime}{ずんだもん:} 参照

\footnotesize \textcolor{pink}{四国めたん:} はい、 参照 は簡単に言うと変数のエイリアス(別名)のことですわ

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

\footnotesize \textcolor{pink}{四国めたん:} についてはtypedefキーワードによりエイリアスを作成できたのを覚えていますか?

\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}{四国めたん:} ちなみに 参照 で重要な点が2つありますわ

  • 型は参照元の変数の型と一致させる
  • 宣言時に参照元となる変数を指定する

\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{pink}{四国めたん:} というわけで、Circleクラスを使ったプログラム例を見ていきましょう

circle.h
#pragma once

#ifndef CIRCLE_H
#define CIRCLE_H

#include <iostream>

const double kPI = 3.14159265358979323846;

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

 public:
  Circle(double diameter) : diameter_(diameter) {}
  ~Circle() {};

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

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

#endif  // CIRCLE_H
hello_world.cpp
#include <iostream>
#include "circle.h"

int main(int argc, char* argv[]) {
  Circle c(10.0);
  Circle& c0 = c;
  std::cout << "円の面積は" << c0.Area() << "cm^2です" << std::endl;
  return 0;
}

参照

\footnotesize \textcolor{pink}{四国めたん:} メイン関数内でCircleのインスタンス"c"の参照として"c0"を宣言、初期化していますわ

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

\footnotesize \textcolor{pink}{四国めたん:} そしてAreaメソッドへのアクセスは参照である"c0"を通しておこなっていますわ

\footnotesize \textcolor{lime}{ずんだもん:} たしかに 参照 を使ってAreaメソッドにアクセスできているのだ

まとめ

\footnotesize \textcolor{pink}{四国めたん:} 参照 についてはここで一旦区切りますわ

\footnotesize \textcolor{lime}{ずんだもん:} 早く続きをお願いするのだ

\footnotesize \textcolor{pink}{四国めたん:} 次回は 参照 を引数などに使う方法をお話ししますわね

\footnotesize \textcolor{lime}{ずんだもん:} おねがいなのだ

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

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

Discussion