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();
}