2014年4月30日水曜日

std::vectorとコピーコンストラクタと仮想関数

#include <iostream>

struct Value
{
	int v;
	Value()
	: v(32)
	{}
	~Value()
	{
		v = 0;
	}
	void Func()
	{
		std::cout << v << std::endl;
	}
};

int main()
{
	Value* p;
	{
		Value a;
		p = &a;
		p->Func();
	}
	p->Func();
	return 0;
}
最初のFuncの呼び出し時は、aは解体されていないので32が出力される。
二回目のFuncでは、aは解体済みなので、エラーになるか、または0が出力される。
#include <iostream>

struct ISample
{
	virtual void Func() = 0;
};

struct Sample : public ISample
{
	void Func()
	{
		std::cout << "Sample::Func" << std::endl;
	}
};

int main()
{
	ISample* p;
	{
		Sample s;
		p = &s;
	}
	p->Func();
	return 0;
}
main関数内のブロックを抜けた時点で、sのデストラクタが呼ばれ、解体される。
Funcを呼び出した時点で、仮想関数テーブル内のデータが失われていた場合、
「純粋仮想関数がコールされた」という、初学者にとっては見慣れないエラーとなって現出する。
#include <iostream>
#include <vector>

struct ISample
{
	virtual void Func() = 0;
};

struct Sample : public ISample
{
	void Func()
	{
		std::cout << "Sample::Func" << std::endl;
	}
};

struct Object
{
	ISample* p;
	Sample s;
	Object()
	: p(&s)
	{}
};

int main()
{
	Object o;
	o.p->Func();
	
	std::vector<Object> ov;
	ov.push_back(Object());
	ov[0].p->Func();
	
	return 0;
}
最初のFuncの呼び出しは問題ない。
二回目、vectorへの要素の追加が問題。
push_back関数の実引数の部分で、まず無名ローカル変数としてObjectのインスタンスが生成される。
そして、vectorが確保している領域へ、コピーコンストラクタによってコピーされる。
ここでコピーコンストラクタが用いられることが問題。
私は、てっきり自分で定義したコンストラクタでvector内の領域が初期化されると思っていたが、
実際にはコピーコンストラクタが呼ばれるので、コピーされたpが指すアドレスは、
コピーされた先のアドレスではなく、元となった無名ローカル変数内のsのアドレスとなる。
このオブジェクトを後で使おうとすると、エラーの原因となる。
もしエラーが出ても、「純粋仮想関数がコールされた」という初学者にとっては一見意味不明なエラーとなる。
このエラーを特定するのに3日近くかかってしまった。

2014年4月29日火曜日

Surface Pro 2を買って失敗したな、と思った話

先日Surface Pro 2を買った。
非常によくできていて、素晴らしい製品なのだが、早くも後悔している。
出来が悪かった訳では無い。
自分のニーズを見誤っていたのだ。

自分は以下の様なニーズでSurface Pro 2を購入した。

  • Windowsを搭載し、コーディングが出来るモバイル環境。
  • 展示等で利用可能な、長時間バッテリー駆動可能なタブレット。
  • ごろ寝で利用出来るタブレット。
実際には、Surface Pro 2はコーディングは出来るし、性能も十分なものの、モバイル環境とは言え、コーディングするには画面が小さく使いづらかった。10.6インチのフルHDディスプレイは等倍では文字が小さくなりすぎて見辛く、かといって文字を拡大するとWindowsのアプリケーションはきちんと対応したアプリケーション以外は多かれ少なかれ表示が崩れてしまう。

展示等での利用には確かに向いているのかも知れないが、数年に一度使うか使わないかなので、そのためにSurface Pro 2を購入する、というのは愚策だった。普通のラップトップを流用しても全然構わなかったはずだ。これに関しては完全に自分の需要を見誤った。

そして、ごろ寝での利用について。Surface Pro 2はごろ寝でも利用出来ないことはないが、Core iタブレットなのでそこそこ重い。また、最も致命的なのが、ブラウジングし辛いことだ。

Windows 8.1で、Modern UIでブラウジングする場合、Internet Explorer以外にマトモな選択肢は無いのだが、このModern UI版Internet ExplorerはAAの表示が崩れるのである。これでは使い物にならない。また、Modern UI版Internet ExplorerはInternet Explorerが規定のブラウザでないと使用出来ない。

そのため、AAが崩れないようにGoogle Chromeを導入したのはいいのだが、Chromeはタッチで全操作を行うにはUIが小さく使いづらい。かといって、画面の拡大率を125%にしてもChromeの表示は拡大されず、150%にして初めて拡大される。
しかしながら、それでもUIは小さく使いづらい上、Chromeでは画面が滲んで表示されてしまう。おそらく、表示崩れを起こさないために、100%のサイズでレンダリングした結果を、150%に拡大表示しているのだろう。

Windowsにおける、UIを拡大するための機能は、どうしてもUIの崩れを引き起こしてしまう。これは、過去のソフトウェア資産を活かすため、互換性を高く保つための設計になっていることが大きな要因の一つだろう。iOSや、Mac OSが、高精細な画面を搭載するにあたって、非対応アプリケーションを丁度倍角でニアレストネイバー法で拡大表示したのが、如何に英断だったか、というのを感じさせられた。これから、スマホ向けの高精細ディスプレイが、より大きなディスプレイに波及していく流れにあたって、どのような大きさでも表示崩れを起こさずに綺麗にレンダリング出来ることは重要になっていくだろう。

以上の様な理由で、Surface Pro 2の購入は失敗だった、と思っている。最初から、普通のラップトップを買えばよかったのだ。