@uents blog

Code wins arguments.

ロベールのC++入門講座でC++を初歩から入門する(6日目)

11章 もっと高く(続き)

「11-12 暗黙の了解」から。思ったより読むのに時間がかかってるなー。そろそろ終わらせたい。。。

暗黙のキャスト

引数が1つのコンストラクタがあれば、それを利用して暗黙のキャストが行われる。抑制したい場合はexplict宣言。

/**
 * @file Explicit.cpp
 */
#include <iostream>
#include <algorithm>
using namespace std;

//#define USE_EXPLICIT

class Foo {
public:
#ifdef USE_EXPLICIT
	explicit Foo(int size)
#else
	Foo(int size)
#endif
	{
		cout << "Foo() called" << endl;
		m_size = size;
		m_array = new int(size);
	}
	virtual ~Foo() {
		cout << "~Foo() called" << endl;
		delete[] m_array;
	}
	int Size() const {
		return m_size;
	}

private:
	int m_size;
	int* m_array;
};

void ShowSize(const Foo& f) {
	cout << f.Size() << endl;
}

int main() {

	ShowSize(4);

	return 0;
}

USE_EXPLICIT が無効な場合はコンパイルが通り、実行結果も期待通り。

% ./Explict
Foo() called
4
~Foo() called

USE_EXPLICIT が有効な場合は暗黙のキャストを防ぐためコンパイルで失敗する。

g++     Explict.cpp   -o Explict
Explict.cpp: In function 'int main()':
Explict.cpp:41: error: invalid initialization of reference of type 'const Foo&' from expression of type 'int'
Explict.cpp:35: error: in passing argument 1 of 'void ShowSize(const Foo&)'
C++のキャスト
  • static_castは静的な普通の型変換
  • reinterpret_castはポインタや参照が関係する強引な型変換
  • const_castはポインタや参照のconstを外す型変換

というわけで、特に下2つあまりおすすめできない、という話。

int n = 1;
const char *p = reinterpret_cast<const char *>(&n);

のように冗長に見えるのもそれはわざとで、基本的にはそういうキャストを推奨しないという表れとのこと。納得。

12章 もっともっと高く

C++というより、Cと共通の話題が多い。

  • ビット演算
  • インライン指定
  • シノニム
  • マクロの活用(関数マクロ、assert)

この辺りはだいたいどれも知ってた。

13章 もっと深く

多重継承

読んで何となく理解。まあ使わないだろうなー。

インターフェースクラス

Objective-Cのインターフェースと同じ話かな。C++の場合、仮想デストラクタ以外は純粋仮想関数しかないクラスをそう呼ぶそう。

typeid演算子とtype_info

型情報を調べるときに。実行時に動的に得られる型情報を「実行時型情報(Run-Time Type Information : RTTI)」と言うらしい。ほほう、覚えとこ。

ダウンキャストとダイナミックキャスト

C++ではアップキャストの逆でダウンキャスト(基底クラスから派生クラスへのキャスト)ができるらしい。でも成功するとは限らない(そりゃそうだ)。成功するかどうかを前もって確認するために、ダイナミックキャストというのがある。こんな感じでやるそう。

// BazはBarの派生クラス

void func(Bar& bar) {
	try {
		Baz baz = dynamic_cast<Baz&> bar;

		// 何らかの処理

	} catch(const bad_cast& e) {
		// ダウンキャスト不可の場合、ここにくる
	}
}
フレンド

friend指定再び。関数でなくクラスにも指定できるよという話。friendクラスはその名の通りフレンドのクラスに自由にアクセスできる。
例として、とあるクラスとそのクラスの生成を行うFactoryクラスでの説明。わかりやすい。

using

usingの真の意味について。基底クラスで作られたメンバ関数を派生クラスで使えたり使えなくしたりでも役に立つ。

テンプレート引数

テンプレート再び。テンプレート引数はtypename以外も取れる。例を見てもよく分からんので書いてみる。

/**
 * @file Template6.cpp
 */
#include <iostream>
#include <vector>
using namespace std;

template <template<typename, typename> class TMPL> class Foo {
public:
	void CheckTypeID() const {
		cout << ((typeid(m_v) == typeid(vector<int>)) ? "true" : "false") << endl;
	}

private:
	TMPL< int, allocator<int> > m_v;
};

int main() {
	Foo<vector> foo; // この場合、m_vはvector<int>となる

	foo.CheckTypeID();

	return 0;
}

実行結果

% ./Template6
true

テンプレート引数には整数も渡せる。

/**
 * @file Template7.cpp
 */
#include <iostream>
using namespace std;

template <int N> class SBit {
public:
	static const int VALUE = 1 << N;
};

int main() {
	cout << SBit<0>::VALUE << ' '
		 << SBit<1>::VALUE << ' '
		 << SBit<2>::VALUE << ' ' << endl;
	return 0;
}
テンプレートの特殊化

クラステンプレートを特定の型に特化した実装定義が可能。これを特殊化という。
言葉じゃわからんので写経。

/**
 * @file Specialization1.cpp
 */
#include <iostream>
#include <climits>
using namespace std;

template <typename TYPE> class Limits {
public:
	static const TYPE MIN;
	static const TYPE MAX;
};

template <> class Limits<unsigned short> { // unsigned shortで特殊化
public:
	static const unsigned short MIN = 0;
	static const unsigned short MAX = USHRT_MAX;
};

// intで特殊化。このように実体定義でも可能
template <> const int Limits<int>::MIN = INT_MIN;
template <> const int Limits<int>::MAX = INT_MAX;

template <typename TYPE> void ShowMinMax() {
	cout << "Type : " << typeid(TYPE).name() << endl;
	cout << " Min : " << Limits<TYPE>::MIN << endl;
	cout << " Max : " << Limits<TYPE>::MAX << endl;
}

int main() {
	ShowMinMax<unsigned short>();
	ShowMinMax<int>();
	return 0;
}
% ./Specialization1
Type : t
 Min : 0
 Max : 65535
Type : i
 Min : -2147483648
 Max : 2147483647

次に部分特殊化の例。部分特殊化とはテンプレート引数に指定した方が等しい場合に特化した宣言を行うこと。これも写経。

/**
 * @file Specialization2.cpp
 */
#include <iostream>
#include <utility>
using namespace std;

template <typename TYPE>
class Value
{
public:
	Value(const TYPE& value) : m_value(value) {};
	void Show() const { cout << m_value << endl; };

private:
	TYPE m_value;
};

// < pair<FIRST, SECOND> > というテンプレート引数に特化
template <typename FIRST, typename SECOND>
class Value< pair<FIRST, SECOND> >
{
public:
	Value(const FIRST& first, const SECOND& second) :
		m_value(first, second) {}
	void Show() {
		cout << "1st : " << m_value.first << endl
			 << "2nd : " << m_value.second << endl;
	}

private:
	pair<FIRST, SECOND> m_value;
};

int main() {
	Value<int> n(42);
	n.Show();

	Value< pair<int, const char*> > p(1,"hoge");
	p.Show();

	return 0;
}
% ./Specialization2
42
1st : 1
2nd : hoge

関数テンプレートは部分特殊化はできないが、オーバーロードで代用することになる。


育児に時間を取られてなかなか進まない。。。次は「13-14 関数ポインタ天国」から。