参照の復習とコピーコンストラクタ

参照とは何か

変数の別名のこと。初級編での参照の説明を思い出しましょう。

「値渡し(あたいわたし, pass by value)」と「参照渡し(さんしょうわたし, pass by reference)」

プログラミング言語において、関数呼び出し時の変数の渡し方には2種類あります。

「値渡し」は、変数のコピーを渡す、ということです。呼び出し先の関数内でコピーをいくらいじっても、呼び出し元の変数は変化しません。コピーを伴うので、大きな変数(オブジェクト)の場合は性能低下が生じる恐れがあります。[教科書3.2 関数へのオブジェクトの引き渡し、3.3 関数からのオブジェクトの返し]

「参照渡し」は、変数の参照を渡す、ということです。変数の参照を渡す場合は、呼び出し先の関数内で変数の参照をいじった場合、呼び出し元の変数が変化します。コピーを伴わないので、大きな変数(オブジェクト)を渡しても性能面で問題は少ないです。[教科書4.7 オブジェクト参照の引き渡し、4.8 参照の返し]

C++言語においては、参照を渡すことが出来ます。一方、参照渡しが無いC言語では、ポインタを値渡しして、呼び出し先の関数内でポインタの中身を操作することで、呼び出し元の変数を操作していました。

また、変数を渡すタイミングは、関数呼び出し時に引数として渡すタイミングの他に、戻り値として返すタイミングがあります。戻り値として参照を返す際には、関数が終了した後も存在している変数の参照を返す必要があります。関数内の一時変数(ローカル変数)の参照を返すと、不定な動作をしますので注意しましょう。

ということで、「参照」はC++言語の重要な機能の一つです。(その他、現代的なプログラミング言語では参照渡しが可能です。)

以下のスライドでは、関数の呼び出し時、戻り時ともに、値渡しであることを確認してください。

参照を返す関数の復習(p.139〜)

戻り値として参照を返す例について以下に説明します。

コピーコンストラクタ(p.154〜)

C++においては、あるオブジェクトを別のオブジェクトにコピーする状況として、”代入”と”コピーによる初期化”の2つがあります。代入の方法を指定するには、次回に学習する”代入演算子のオーバーロード”を行います。ここでは触れません。

”コピーによる初期化”は以下の場合に発生します。

  1. 新しいオブジェクトを宣言する際、既存のオブジェクトを元にして作成するとき
  2. 関数にオブジェクトを渡すとき(関数呼び出し時に引数を値渡しする場合)
  3. 関数の戻り値として使用するオブジェクトを作るとき(関数の戻り値を値渡しする場合)

以下の例では、”コピーによる初期化”が3回、”代入”が1回発生しています。

この際、C++のデフォルトでは、単純なビット単位のコピーにより、まったく同じオブジェクトが作られます。

LIST1-4 (point.cpp)

実行例

1,2
1,2
2,3

ただし、単純なコピーだと、ちょっと困った状況が起こる場合がありますので注意です。以下のスライドで説明します。

つまり、コピーによる初期化の際に、オブジェクトがポインタを含む場合、単純なコピーでは問題が生じる場合があるわけです。

コピーコンストラクタを定義すると、単純なコピーではない、「コピーによる初期化」の方法を指定することができます。

【復習】初期化の方法を実装するのはコンストラクタでしたね。デフォルトのコンストラクタは、引数を取らないコンストラクタです。また、初期化する際にパラメータを指定したい場合には、何らかの引数を取るコンストラクタも定義できます。

コピーコンストラクタは、引数として同じクラスのオブジェクトの参照をとる、コンストラクタです。

課題

課題1−5

以下は、初級編で使用した文字列クラスのソースコードをもとに作成したものである。

必須 (このプロジェクトは再度使用するので、必ず完成させておくこと。)

  1. プロジェクト名をp3kadai2w1として、新規作成し、以下の3つのファイルをプロジェクトに追加しなさい
  2. 出力の部分(printfを使っている部分)をcoutを使うように変更しなさい。
  3. 改行は\nではなく、endlを使うよう変更しなさい。
  4. i 文字目の文字の参照を返すメンバ関数getCharを追加しなさい。
  5. コピーコンストラクタを追加しなさい。
  6. 4と5で追加した機能が正しく動作しているか確認しなさい。

余力のある人は

  • getCharを改良し、i が文字列長の範囲外だった場合エラーメッセージを出力してプログラムを終了するよう変更しなさい。
  • getCharを参照を返さないように変更するとどうなるか試してみなさい。
  • コピーコンストラクタをコメントアウトすると何が起きるか試してみなさい。
  • cinを使って、ストリーム演算子(>>)で文字列を入力できるよう変更しなさい。
  • coutを使って、ストリーム演算子(<<)で文字列を出力できるよう変更しなさい。(printStringメソッドと同じ内容)

ヒント

  • 入力された文字列を一時保管するための配列が必要。(例: console3.cppの23行目 char s[10];)
  • 入力された文字列をStringクラスのメンバ変数sに格納するために、void setString(const char* n)のようなメンバ関数を作成する。

MyString.h

MyString.cpp

main.cpp

補足説明

補足説明用スライド