読者です 読者をやめる 読者になる 読者になる

virtual……仮想継承ってなあに

 ちまたで、virtualとか見かけてついeclipseでコードをかいてみたので書いてみる。
 「仮想仮想とでてきて、C++は仮想が大好きですね」*1
 とか思いますが仮想継承というのは、多重継承をしない限りいらないので覚えなくて良いです。
 どうしても多重継承したい! というときに使うかもしれません。

 で、多重継承。
 Hoge : Foo, Barと多重継承した場合、FooとBarのインスタンスは別物として扱われます。
 要するにFooとBarが同名のメンバ変数やメンバ関数を持っていてもそれは別物です。
 なので、
Foo : Base,
Bar : Base,
Hoge : Foo, Bar
 という多重継承をした場合にBaseのインスタンスはFoo::Baseと、Bar::Baseとがあって、Hogeはその両方を持ちます。
 でも、恐らくこういう場合に設計者はBaseのインスタンスを共通で利用したいと考えている筈です。(多くの場合)

 で、これを解決するのが仮想継承です。

class File {
protected:
	unsigned char* _file;
	int _cursor;
public:
	File() {
		_file = new unsigned char[100];
		_cursor = 0;
	}
	virtual ~File() {
		delete _file;
	}
	void reset() {
		_cursor = 0;	
	}
};

class OutputFile : virtual public File {
public:
	unsigned char read() {
		return _file[_cursor++];
	}
};

class InputFile : virtual public File {
public:
	void write(unsigned char data) {
		_file[_cursor++] = data;
	}
};

class SaveDataFile : public OutputFile, public InputFile {
};	

int main(int argc, char *argv[])
{
	SaveDataFile* file = new SaveDataFile();
	file->write(1);
	file->write(2);
	file->write(3);
	file->write(4);
	file->write(5);
	file->reset();
	for (int i = 0; i < 5; i++ ) {
		unsigned char data = file->read();
		printf( "[%d]?n", data );
	}
}

 糞の役にもたたないコードですが、こういう感じで使えます。
 Fileは何かのファイル(操作)を抽象化したもので、それにInputとOutputをするための継承があり、
 その両方をサポートしているSaveDataFileがあるということです。

 この継承をvirtualにしない場合、Fileのメンバはすべて個々に継承されるため、
 例えば、resetを呼び出そうとすると
 「だんなさん、どっちを呼んだらいいかわからねえっすよ」とコンパイラおばさんに怒られる羽目になります。
 こんなのあんまりかかないと思うけど。
 こういうのを考えたりしないといけないので、C++はめんどいです。
 ある意味、危険で自由ですが。
 僕もまるでわかりません。

*1:仮想をつけるとオーバーヘッドがあるのであえてつけてない設計です