swap処理まとめ
Javaを勉強中の為一端まとめの為にJava、C言語、C++それぞれの
swap関数を作ってのswap処理についてまとめてみる
※あくまで例なのでアクセサとかアクセス制限とかについては考えません
プリミティブ型は値渡ししか出来ない為クラスを作って参照型としてラップして渡す必要がある class MyInteger { public int value; MyInteger(int value) { this.value = value; } public String toString() { return Integer.toString(value); } }; swap処理はクラスを参照型として渡し、内部的に入れ替えを行う、めんどい void mySwap(MyInteger x, MyInteger y) { int temp = x.value; x.value = y.value; y.value = temp; } 使い方 public static void main(String[] args) { MyInteger x = new MyInteger(3); MyInteger y = new MyInteger(5); System.out.println(x + " " + y); mySwap(x, y); System.out.println(x + " " + y); }
C
ポインタで渡す void my_swap(int* x, int* y) { int temp = *x; *x = *y; *y = temp; } 使い方 void main() { int x = 3; int y = 5; printf("%d %d\n", x, y); my_swap(&x, &y); printf("%d %d\n", x, y); }
構造体の場合はメンバ変数の値コピーのオーバーヘッドを無くすためにも
アドレスの交換で済ましたい、これはC++のクラスと一緒に書く
ポインタで渡すのはCと一緒 参照で渡す void MySwap(int& x, int& y) { int temp = x; x = y; y = temp; } 使い方 void main() { int x = 3; int y = 5; std::cout << x << " " << y << std::endl; MySwap(x, y); std::cout << x << " " << y << std::endl; }
クラスの場合、構造体も仕組み的に一緒
渡したいクラス class MyClass { public: int value; MyClass(int value) : value(value) { std::cout << "コンストラクタ!" << std::endl; } ~MyClass() { std::cout << "デストラクタ!" << std::endl; } }; ポインタのポインタを引数に渡してポインタの交換をすることにより 値コピーのオーバーヘッドをなくす、参照渡しだと上手くいかない void MySwap(MyClass** x, MyClass** y) { MyClass* temp = *x; *x = *y; *y = temp; } 使い方 void main() { MyClass* x = new MyClass(3); MyClass* y = new MyClass(5); std::cout << x->value << " " << y->value << std::endl; MySwap(&x, &y); std::cout << x->value << " " << y->value << std::endl; delete x; delete y; }
明示的な解放がめんどいしポインタのポインタがややこしいので
スマートポインタと参照渡しですっきり
クラスは同じ スマートポインタの場合は参照渡しでもOK std::swapでもOK template <class T> void MySwap(T& x, T& y) { T temp = x; x = y; y = temp; } 使い方 void main() { std::auto_ptr<MyClass> x(new MyClass(3)); std::auto_ptr<MyClass> y(new MyClass(5)); std::cout << x->value << " " << y->value << std::endl; MySwap(x, y); std::cout << x->value << " " << y->value << std::endl; }
std::auto_ptrは"="による代入時の所有権が移る辺りの動作を考えるのがめんどい且つ
速度が少し遅くなっても気にしないって場合
クラスは同じ swap関数も同じ 使い方 void main() { boost::shared_ptr<MyClass> x(new MyClass(3)); boost::shared_ptr<MyClass> y(new MyClass(5)); std::cout << x->value << " " << y->value << std::endl; MySwap(x, y); std::cout << x->value << " " << y->value << std::endl; }
ついでにC++/CLI
ちょっと増えたよ ref class MyClass { public: int value; MyClass(int value) : value(value) { std::cout << "コンストラクタ!" << std::endl; } ~MyClass() { std::cout << "デストラクタ!" << std::endl; } !MyClass() { std::cout << "ファイナライザ!" << std::endl; } }; swap関数はC++の参照渡し引数と同じ gcnewしたクラスは参照型になるのでswap関数内で"="で代入してるけども GCで管理してくれるのでstd::auto_ptrの時みたいに所有権を気にする必要無し void main() { MyClass^ x = gcnew MyClass(3); MyClass^ y = gcnew MyClass(5); std::cout << x->value << " " << y->value << std::endl; MySwap(x, y); std::cout << x->value << " " << y->value << std::endl; }
それぞれの実行結果
3 5 5 3
C++のクラスVer
コンストラクタ! コンストラクタ! 3 5 5 3 デストラクタ! デストラクタ!
コンストラクタ! コンストラクタ! 3 5 5 3 ファイナライザ! ファイナライザ!
C++でboost::shared_ptrを使うかC++/CLIを使うのがすっきり、C#は無勉なので知らない。
Javaはswap関数の実装を見るに限り良くも悪くも言語仕様のシンプルさが目立つ。