Contents
構造体を packed 属性でぐしゃっとつぶして、alignment が崩れている (可能性がある)場合に、メンバ変数を引数を呼び出し先の 関数で、参照型でバインドしようとすると、コンパイラのエラーになる (g++-3.4.4, g++-4.1.1 で確認)。:
#include <iostream> struct A { int b; } __attribute__ ((packed)); void func(int& val) { std::cout << "val = " << val << std::endl; val *= 2; } int main() { A a; a.b = 10; // // func(a.b); // // This code causes the compile error on g++-4.1.1. // // error: cannot bind packed field 'a.A::b' to 'int&' // int& x = a.b; func(x); std::cout << "val = " << a.b << std::endl; }
上のコードのコメント内の func(a.b) ではコンパイルが通らない。 で、int& x = a.b; func(x); として一旦呼び出し側で明示的に 一時的な参照型の変数に格納しておいてやる。これだとコンパイルが 通る。けど、これはコンパイラがやってくれても、いいじゃん。 Visual C++ はやってくれるよ。
今更ながら、char, short, unsigned int, long longなどの整数型を比較 する時のsigned/unsignedのミスマッチが気になる。:
short us = -1; unsigned int i = 1; if (us < i) { std::cout << "Expected" << std::endl; } else { std::cout << "Unexpected" << std::endl; }
これで、コンパイル時に warning が出たりするけれど、たとえば warning が 何百とでて(C++ではありがち)とりあえずほっといたりすると、痛い目に 合う。
メンバ関数はconstでオーバーロードできるので、メンバ関数へのポインタだって 別だけど、どうやって書くの? こう。:
#include <iostream> class TestClass { public: void print() { std::cout << "Non Const" << std::endl; } void print() const { std::cout << "Const" << std::endl; } }; int main() { typedef void (TestClass::*memfun_t)(); typedef void (TestClass::*memfun_const_t)() const; memfun_t memfun1 = &TestClass::print; memfun_const_t memfun2 = &TestClass::print; TestClass test; (test.*memfun1)(); (test.*memfun2)(); }
try-catch で例外をちゃんと受け取らないと、プログラムは abort してしまうので、
とかいう関数でユーザ定義の処理にすることができる。これは libc の関数ではなく、C++ の言語仕様なので安心して 利用可能。
まとめ:
exception <----- logic_error <----- domain_error <----- invalid_argument <----- length_error <----- out_of_range <----- runtime_error <----- range_error <----- overflow_error
代入演算子については、C++ の言語として他の算術演算子 * や == といった ものから区別され、大域関数ではオーバーロードできない。non static な メンバ関数でないといけない。
ユーザ定義の後置演算子 ++ は、前置演算子 ++ と区別するために:
Object & operator ++ () Object operator ++ (int)
として宣言される。int はダミーの引数なので使わない。 「プログラミング言語 C++」第3版 11.11参照。
class X; new X とかでオブジェクトの領域を確保して、初期化することは 基本中の基本だけど、あらかじめ割り当てておいた領域に新たなオブジェクト を作成することができる。 「プログラミング言語 C++」第3版 10.4.11参照。:
char buf[1024]; new (buf) X
勉強不足の STL はメモメモ。int 型の vector<int> のソートは、 例がたくさん見つかったけど、任意の構造体へのポインタ(vector<T*>) の場合は以下の通り。:
#include <iostream> #include <vector> #include <algorithm> #include <functional> template<typename T> struct less_ptr: public ::std::binary_function<T *, T *, bool> { public: bool operator () (const T * lhs, const T * rhs) { return *lhs < *rhs; } }; class A { int m_val; public: A(int val): m_val(val) {} bool operator < (const A & rhs) const { return m_val < rhs.m_val; } int val() { return m_val; } }; template<typename T> bool greater_ptr (T * lhs, T * rhs) { return ! ((*lhs) < (*rhs)); } int main() { typedef ::std::vector<A *> AList; AList alist; alist.push_back(new A(10)); alist.push_back(new A(1)); alist.push_back(new A(5)); alist.push_back(new A(3)); alist.push_back(new A(4)); // 関数ポインタアダプタを使ったソート // std::sort(alist.begin(), alist.end(), std::ptr_fun(&greater_ptr<A>)); for (AList::iterator i = alist.begin(); i < alist.end(); ++i) { std::cout << (**i).val() << " "; } std::cout << std::endl; // 便利テンプレートをつくってみた // std::sort(alist.begin(), alist.end(), less_ptr<A>() ); for (AList::iterator i = alist.begin(); i < alist.end(); ++i) { std::cout << (**i).val() << " "; } std::cout << std::endl; }
二重字というのは、&&(and)、|(bitor)などの記号の別の表現方法。 (「プログラミング言語 C++」第3版 C.3.1参照)。ASCII の特殊文字の 位置に、キャラクタセットが割り当てられている場合の回避策として つかわれる。例えば、'&'は "<:"、'|'は":>"に置き換えられる。
これが以下のコンパイルエラーを引き起こす。:
template< template <class> class ThreadingModel > class MultithredingClass { ... } MultithreadingClass<::Loki::ClassLevelLockable> object;
<: が二重字として解釈され、意味不明のエラー出力を頂戴する。 最後の行を以下に変更。:
MultithreadingClass< ::Loki::ClassLevelLockable> object;
のようにスペースを一個いれておく。型名をマクロの引数に する場合にも気を付ける。
コンストラクタも一応テンプレートにできる模様(g++-2.95.3, g++-3.3)。 あたりまえと言えばあたりまえなのか?
任意のクラスを継承するときには、Class B みたいにパラメータの数だけ コンストラクタを書いておくと便利かも。:
#include <iostream> #include <string> class A { public: A() {} A(const std::string & str): m_str(str), m_int(0) {} A(const std::string & str, const int n): m_str(str), m_int(n) {} void print_str () { std::cout << m_str << std::endl; } void print_int () { std::cout << m_int << std::endl; } private: const std::string m_str; const int m_int; }; template<typename Base> class B: public Base { public: B () {} template<typename P1> B (P1 p1): A(p1) {} template<typename P1, typename P2> B (P1 p1, P2 p2): A(p1, p2) {} }; int main() { B<A> *b1 = new B<A>("Hello, world!!"); B<A> *b2 = new B<A>("Good-bye world!!", 10); b1->print_str(); b2->print_int(); }
仮想関数のない継承関係の場合は、"4base" と表示される。:
#include <iostream> #include <typeinfo> using namespace std; class base { public: // virtual ~base() {} }; class derived: public base { }; int main() { base * p = new derived; cout << typeid(*p).name() << endl; }
ところが、仮想関数を実装すると、"7derived" と表示される。:
#include <iostream> #include <typeinfo> using namespace std; class base { public: virtual ~base() {} }; class derived: public base { }; int main() { base * p = new derived; cout << typeid(*p).name() << endl; }
「プログラミング言語C++ 第3版」の C.13.6 「限定子としてのtemplate」に 書いてあったことなんだけど、はまりまくったのでメモ。
以下のコード "T::print<U>()" の部分がコンパイルエラー。なぜならば コンパイラは T::print < U と解釈して、「小なり」演算子と勘違いする。 なので、コンパイラに「print は template なんだよ」と教えてやらなければ ならない。typename っていうのはあるが、template を限定子として使うとは しらなかった。Loki のソースは読むべし。
なので、"T::template print<U>()" に書き直せばよい。:
#include <iostream> #include <string> #include <typeinfo> using namespace std; class PrintCout { public: template<typename T> void print() { cout << typeid(T).name() << endl; } }; class PrintCerr { public: template<typename T> void print() { cerr << typeid(T).name() << endl; } }; template<typename T> class PrintTypename { public: template<typename U> void print() { T::print<U>(); } }; int main() { PrintTypename<PrintCout> obj; obj.print<int>(); obj.print<char>(); obj.print<double>(); }
以下、書き直したソース:
#include <iostream> #include <string> #include <typeinfo> using namespace std; class PrintCout { public: template<typename T> static void print() { cout << typeid(T).name() << endl; } }; class PrintCerr { public: template<typename T> static void print() { cerr << typeid(T).name() << endl; } }; template<typename T> class PrintTypename { public: template<typename U> void print() { T::template print<U>(); } }; int main() { PrintTypename<PrintCout> obj; obj.print<int>(); obj.print<char>(); obj.print<double>(); }