1.GCCでは未対応のケースがある。定義時にexportをつけてもだめな場合はコンパイ
ル時に-c -frepoをつけ、生成したオブジェクト(.o)と.rpoファイルをリンク
(gcc foo.o boo.o foo.rpo boo.rpoなど)する。
2.定義するファイルで、テンプレートインスタンスを明示的に生成する。
template class A<int>;
template ostream& operator << (ostream&, const A<int>&);
テンプレート利用用途がわかっている場合こちらがいい。
(GCC Manual - Using and Porting the Gnu Compiler Collection(GCC)より。
・
第三引数にクラス内の非静的関数を指定すると発生する。mem_func(foo)を使うか、比較関数にstaticをつけるかクラスの外に出せば解決。どのインスタンスの関数を指定したのかが不明確になるのがエラーの原因。
・
宣言のみのクラス(T)をテンプレートで間接的に呼び出すとデストラクタのエラーや
instantiation of '〜<T>'などというエラーが出る。定義の前に(ポインタではなく
実体で)利用するクラスは実装しておかなければならない。うっかり失敗しても気づ
きにくいので注意。
・
virtualを使うクラスではデストラクタもvirtualにしなければならない、というのは常識でもありますが、仮想デストラクタにしなかった場合、具体的にどういう問題が発生するのか?と聞かれると曖昧になる人も居ると思います。そこで、簡単なコードを書いてみました。
---test.cpp---
#include <iostream>
int* pBlank = 0;
class test{
public:
test(){
std::cout << "test1 constructed" << std::endl;
}
~test(){ // ※1
std::cout << "test1 destructed" << std::endl;
}
void* operator new(size_t sz){
std::cout << "test1 new called." << std::endl;
return new int();
}
void operator delete(void* p){
std::cout << "test1 delete called." << std::endl;
delete static_cast<int*>(p);
}
};
class test2 : public test{ // testを継承
public:
test2(){
std::cout << "test2 constructed" << std::endl;
}
~test2(){
std::cout << "test2 destructed" << std::endl;
}
void* operator new(size_t sz){
std::cout << "test2 new called." << std::endl;
return new int();
}
void operator delete(void* p){
std::cout << "test2 delete called." << std::endl;
delete static_cast<int*>(p);
}
};
int main(){
test2* pt2 = new test2(); // test2オブジェクトをヒープに作成
test* p=pt2; // test2の親クラスtestのポインタpをpt2にする
delete p; // testクラスのポインタとしてヒープを解放
}
この結果は次のようになります。
test2 new called.
test1 constructed
test2 constructed
test1 destructed // test2デストラクタが呼び出されない!
test1 delete called. // test1のdeleteが呼び出されてしまう!
…と、ひどい結果ですね。正しいデストラクタが呼ばれないばかりか、ヒープ解放にtestのdeleteを使っています。子クラスが親より広い領域を使うなら当然メモリリークになりますし、子クラスで独自の終了処理(ファイルにデータを書き込んで終了とか)をしたい場合などにも実行されません。デストラクタについては
※1の行の始め、つまり親クラスのデストラクタの接頭語としてvirtualをつければいい、というのがおわかりでしょう。では、deleteを正しく呼び出すにはどうすればいいのでしょう? virtual void operator deleteとすれば、と考えるかもしれませんが、operator deleteは個々のオブジェクトに対してではなくクラス自体の処理として作用する、すなわちstaticなのでvirtualを使うとコンパイルエラーが出ます。ではどうすれば…と悩むところですが、実はデストラクタにvirtualをつけることでdeleteも適切に選択するようになり、次のように思惑通りの結果になるのです!
test2 new called.
test1 constructed
test2 constructed
test2 destructed
test1 destructed
test2 delete called.
というわけで、子クラスを持つ可能性のあるクラスのデストラクタには必ずvirtualをつけましょうね。