火曜日, 5月 14, 2013

C++ - メモリと対話する楽しみ

C++

周りの新人や,面接等をしている限りひしと感じるのは,とにかくC/C++を知っている人がどんどん減ってきていることである.私はJavaをやっていて..というのが8割方,9割,いや,もうほとんど10割である.時代の流れである.この流れをとやかく言うつもりはないし,ニーズも多いのは間違いない.なにしろC++に比べて非常に簡単に書ける.Javaならではの難しさだって多々ある.しかし,JVMの大変優れたGCや素晴らしいオープンソースライブラリのおかげでJavaの開発は他の言語,特にC/C++に比べて随分楽であることは間違いないだろう.

しかし,Javaを書いてもメモリと対話している感じはあまり感じない ( あくまでこれは私個人の一意見 ).勿論プロファイリングをしているときにヒープやpermgenの使用率をじーっとみているときはちょっとはメモリと対話してる感はあるが..C/C++の比ではない.
つまるところ,Javaは物足りなさを感じるのである.幸い私のプロジェクトはJavaとC++が入れ代わり立ち代わりなので両方を楽しむことはできているが,やはりC++を離れてJavaばかりやっているとC++に無性に戻りたくなるのである.

ということでメモリを意識できる例の一つであるC++のポリモーフィズムの仕組みを紹介する.

C++のポリモーフィズム

以下のC++のコードは実に変哲もないポリモーフィズムのコードである.が,その仕組みは関数のメモリアドレスルックアップを含む,実にC++らしいものである.

#include <iostream>
#include <iostream>
#include <cstdlib>
#include <vector>
#include <string>
using namespace std;

class Parent {

protected:
 vector<int> v;

public:
 Parent(){ cout << "constructor" << endl; }
 virtual ~Parent() { cout << "Destructor" << endl; }

 virtual void push_back(int a){ v.push_back(a); }
 //virtual void push_back(int a){ v.push_back(a); }
 virtual int get(int idx){ return v.at(idx); }

};

class Child: public Parent {

private:
 string decoration;
public:

 Child(string decorationVal="empty") { decoration = decorationVal; }

 int get(int idx){ cout << "**" << decoration << "**" << endl; return v.at(idx); }

};

int main() {

 
 Parent *p = new Child(string("takoneko"));
 p->push_back(32);
 p->push_back(16);
 cout << p->get(0) << endl;
 return 0;

}

ここではvirtual修飾子がキーである.これをつければその関数は仮想関数ということになり,その修飾子がないメソッドは非仮想関数となる. コンパイラが上記クラスをコンパイルした場合はvtableという不可視のフィールドがそのクラスに足される.そして仮想関数はvtableにエントリされる.仮想関数はvtableにエントリされない.仮想関数が呼ばれた場合は動的結合(Dynamic Binding)という仕組みで呼び出される.これはランタイム時にvtableのルックアップを行い,呼び出すべき関数のメモリアドレスを取得する.一方非仮想関数が呼ばれた場合は静的結合(Static Binding)という仕組みで呼び出される.静的結合は単純にその関数を呼び出したポインタの型であるクラスの関数を呼ぶ.このバインディングはコンパイル時に決定するので動的結合より早い.
ちなみにJavaでは全て仮想関数.protected void init()とかをJavaで書けばC++でいうところのprotected virtual void init()と同じである.

0 件のコメント: