ロベールの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);
のように冗長に見えるのもそれはわざとで、基本的にはそういうキャストを推奨しないという表れとのこと。納得。
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 関数ポインタ天国」から。