目次
はじめに
C++で、ある関数の中でクラスのオブジェクトを利用したい場合、どのようにしてそのオブジェクトを関数に渡せばよいでしょうか? C++には、主に3つの方法があり、それぞれにメリットとデメリットが存在します。
- 値渡し (Pass by Value): オブジェクトのコピーを作成して渡す、最もシンプルな方法。
- ポインタ渡し (Pass by Pointer): オブジェクトのメモリアドレスを渡す、C言語由来の伝統的で効率的な方法。
- 参照渡し (Pass by Reference): オブジェクトの参照を渡す、C++で推奨される、安全で効率的な方法。
この記事では、これら3つの方法について、それぞれの仕組みと使い方、そして使い分けのポイントを解説します。
3つの渡し方のサンプルコード
このコードは、Product
(商品)オブジェクトを、3つの異なる渡し方をする関数にそれぞれ渡して、同じ結果を表示するものです。
完成コード
#include <iostream>
#include <string>
using namespace std;
class Product {
private:
int id;
string name;
public:
Product(int i, string n) : id(i), name(n) {} // コンストラクタ
int getID() const { return id; }
string getName() const { return name; }
};
// 1. 値渡し (Pass by Value)
void processByValue(Product item) {
cout << "ID: " << item.getID() << ", 商品名: " << item.getName() << " (値渡し)" << endl;
}
// 2. ポインタ渡し (Pass by Pointer)
void processByPointer(const Product* p_item) {
cout << "ID: " << p_item->getID() << ", 商品名: " << p_item->getName() << " (ポインタ渡し)" << endl;
}
// 3. 参照渡し (Pass by Reference)
void processByReference(const Product& r_item) {
cout << "ID: " << r_item.getID() << ", 商品名: " << r_item.getName() << " (参照渡し)" << endl;
}
int main() {
Product myProduct(101, "高機能マウス");
// 各関数を呼び出す
processByValue(myProduct);
processByPointer(&myProduct);
processByReference(myProduct);
return 0;
}
1. 値渡し (Pass by Value)
void processByValue(Product item);
- 仕組み: 関数を呼び出す際に、オブジェクト
myProduct
の完全なコピーが作成され、それが関数の仮引数item
に渡されます。 - メリット:
- 関数内で何をしても、元の
myProduct
には一切影響がありません。
- 関数内で何をしても、元の
- デメリット:
- オブジェクトのサイズが大きい場合、**コピーのコスト(時間とメモリ)**が無視できなくなります。
2. ポインタ渡し (Pass by Pointer)
void processByPointer(const Product* p_item);
- 仕組み: オブジェクトそのものではなく、それが格納されているメモリアドレスだけを関数に渡します。
- 呼び出し:
processByPointer(&myProduct);
のように、アドレス演算子&
を付けて呼び出します。 - アクセス: 関数内では、アロー演算子
->
を使ってメンバにアクセスします。 - メリット:
- 大きなオブジェクトを渡す場合でも、アドレスを渡すだけなので非常に効率的です。
- デメリット:
- 構文が少し複雑になります。
- ポインタが
NULL
である可能性(ヌルポインタ)を考慮する必要があります。
3. 参照渡し (Pass by Reference)
void processByReference(const Product& r_item);
- 仕組み: 内部的にはポインタと似ていますが、関数側では通常の変数と同じように扱える、C++の便利な機能です。
- 呼び出し:
processByReference(myProduct);
のように、値渡しと同じ構文で呼び出せます。 - アクセス: 関数内では、ドット演算子
.
を使ってメンバにアクセスできます。 - メリット:
- ポインタ渡しと同様に、コピーが発生しないため非常に効率的です。
- 値渡しとほぼ同じシンプルな構文で記述できます。
const
の役割: 引数をconst Product&
とすることで、関数内で誤って元のオブジェクトの値を変更してしまうことを防ぎます。読み取り専用で渡す場合の定石です。
まとめ
関数にオブジェクトを渡す3つの方法を解説しました。
方法 | 効率 | 構文のシンプルさ | C++での推奨度 |
値渡し | △ (コピーが発生) | ◎ (最もシンプル) | △ (小さなオブジェクトのみ) |
ポインタ渡し | ◎ (高速) | △ (構文が複雑) | ◯ (C言語との互換性) |
参照渡し | ◎ (高速) | ◎ (シンプル) | ◎ (最も推奨) |
結論として、現代のC++では、パフォーマンスと書きやすさのバランスが最も良い「const
参照渡し」を第一の選択肢として考えるのがベストプラク-ティスです。