Blog
C++の疑問: 値渡し、参照渡し、ポインタ渡し
※ 末尾に追記あり。
C++、いろいろ未だにわからにことだらけなのだけれど、関数に値を送る場合の
- 値渡し
- 参照渡し
- ポインタ渡し
の違いがいまだにすっきりと整理できていない。
C++、自分の場合はoFで使うことがほとんどなので、oFのコードで実験してすっきりしてみようとシンプルなコードを書いてみた。
力をベクトルで与えると運動するParticleというクラスを以前から作っていたいので、これはそのまま流用。長くなるのでコードの内容は割愛。
Particleを操作するためのMoverというクラスをつくってみた。このMoverクラスは3種類の方法でParticleのインスタンスを受けとることができる
- A: 値渡し
- B: 参照渡し
- C: ポインタ渡し
Mover.h
#pragma once #include "ofMain.h" #include "Particle.h" class Mover{ public: void setParticle(Particle p); // 値渡し void setParticleRef(Particle &p); // 参照渡し void setParticlePtr(Particle *p); // ポインタ渡し void update(); Particle particleA; Particle particleB; Particle *particleC; };
Mover.cpp
#include "Mover.h" // A: オブジェクトの値渡し void Mover::setParticle(Particle p){ particleA = p; particleA.velocity = ofVec2f(10.0, 0); } // B: オブジェクトの参照渡し void Mover::setParticleRef(Particle &p){ particleB = p; particleB.velocity = ofVec2f(10.0, 0); } // C: オブジェクトのポインタ渡し void Mover::setParticlePtr(Particle *p){ particleC = p; particleC->velocity = ofVec2f(10.0, 0); } // それぞれのオブジェクトを更新 void Mover::update(){ particleA.resetForce(); particleA.update(); particleB.resetForce(); particleB.update(); particleC->resetForce(); particleC->update(); }
これを、ofAppから呼びだしてみる。
ofApp.cpp
#include "ofApp.h" //-------------------------------------------------------------- void ofApp::setup(){ ofSetFrameRate(60); ofBackground(0); // パーティクルの初期位置を設定 particleA.position = ofVec2f(10, 10); particleB.position = ofVec2f(10, 110); particleC.position = ofVec2f(10, 210); // A: オブジェクトの値渡し mover.setParticle(particleA); // B: オブジェクトの参照渡し mover.setParticleRef(particleB); // C: オブジェクトのポインタ渡し mover.setParticlePtr(&particleC); } //-------------------------------------------------------------- void ofApp::update(){ mover.update(); } //-------------------------------------------------------------- void ofApp::draw(){ // A: 値渡しの描画 ofSetColor(ofColor::red); particleA.draw(); // B: 参照渡しの描画 ofSetColor(ofColor::blue); particleB.draw(); // C: ポインタ渡しの描画 ofSetColor(ofColor::yellow); particleC.draw(); }
実行結果はどうなったかというと
- A: 値渡し → 動かない
- B: 参照渡し → 動かない
- C: ポインタ渡し → 動いた
Aの値渡しが動かないのは、まあ予想内の挙動。そして、Cのポインタ渡しでは狙い通りofAppのParticleのインスタンスもMoverから更新され、無事動いた。
解せないのは、Bの参照渡しの挙動。参照渡しはオブジェクトの実体ではなく、あくまでその参照として渡っているので、ofApp側のインスタンスも連動して更新されると考えていたのだが、このコードではそうならなかった。うーん、何故なんでしょう。
C++はムズカシイネ…
プロジェクト全体は下記にあります。
【追記】: Facebookでコメントしていただき、参照渡しがうまくいかなかった原因判明。Mover.cppの11行目で、”particleB = p;”と代入している。ここでpをparticleBにコピーしてしまっていて、その結果参照から外れてしまったということのようだ。うーん、なるほど。参照渡しはクラスのメンバとして扱うのは難しいですね…
こう修正したら、参照渡しでも動きました。クラス変数を参照渡しするには、コンストラクタの初期値として渡す必要あるみたい。ううむ、扱い難しいですね。
Mover.h
#pragma once #include "ofMain.h" #include "Particle.h" class Mover{ public: // 参照渡しは、コンストラクタのイニシャライザで Mover(Particle &particleB):particleB(particleB){}; void setParticle(Particle p); // 値渡し void setParticlePtr(Particle *p); // ポインタ渡し void update(); Particle particleA; Particle &particleB; Particle *particleC; };
Mover.cpp
#include "Mover.h" // A: オブジェクトの値渡し void Mover::setParticle(Particle p){ particleA = p; particleA.velocity = ofVec2f(10.0, 0); } // C: オブジェクトのポインタ渡し void Mover::setParticlePtr(Particle *p){ particleC = p; particleC->velocity = ofVec2f(10.0, 0); } // それぞれのオブジェクトを更新 void Mover::update(){ particleA.resetForce(); particleA.update(); particleB.resetForce(); particleB.update(); particleC->resetForce(); particleC->update(); }
ofApp.cpp
#include "ofApp.h" //-------------------------------------------------------------- void ofApp::setup(){ ofSetFrameRate(60); ofBackground(0); // パーティクルの初期位置を設定 particleA.position = ofVec2f(10, 10); particleB.position = ofVec2f(10, 110); particleB.velocity = ofVec2f(10, 0); particleC.position = ofVec2f(10, 210); // B:参照渡しは、コンストラクタで渡す mover = new Mover(particleB); // A: オブジェクトの値渡し mover->setParticle(particleA); // C: オブジェクトのポインタ渡し mover->setParticlePtr(&particleC); } //-------------------------------------------------------------- void ofApp::update(){ mover->update(); } //-------------------------------------------------------------- void ofApp::draw(){ // A: 値渡しの描画 ofSetColor(ofColor::red); particleA.draw(); // B: 参照渡しの描画 ofSetColor(ofColor::blue); particleB.draw(); // C: ポインタ渡しの描画 ofSetColor(ofColor::yellow); particleC.draw(); }