2012-04-10 23 views
1

私はC++でテンプレート構造のバイナリ検索ツリーを開発しています。今まではすべてがうまくいっています。この問題は、私が慣れ親しんでいない、ぎこちないC++のものを扱っています。私は助けが必要です。C++関数ポインタのキャスト

これまで、ツリーを走査し、各ノードを異なる順序で訪問する関数を定義しました。ここで問題となるのは以下のように定義される。

TreeNode.h

public: 
    static void PostOrderVisit(TreeNode<T>* node, void visit(const T& v)); 

TreeNode.cpp

template <class T> void TreeNode<T>::PostOrderVisit(TreeNode* node, void visit(const T& v)) { 
    if (node->leftChild != NULL) 
    PostOrderVisit(node->leftChild, visit); 
    if (node->rightChild != NULL) 
    PostOrderVisit(node->rightChild, visit); 
    visit(node->value); 
} 

これは、ノードを作成し、静的PostOrderVisitを呼び出すテストプログラムで正常に動作します。

フレンドクラス(BinSTree.h/cpp)では、ツリー内のすべてのノードを削除するメソッドを実装していますので、この訪問者を使用してDelete()関数を呼び出すことをお勧めします。各ノード(Delete()関数もBinSTreeのテストプログラムで正常に機能します)。

この関数は、以下のように定義されています。

template <class T> void BinSTree<T>::ClearTree() { 
    TreeNode<T>::PostOrderVisit(this->root(), &BinSTree<T>::Delete); 
} 

ここに問題があります。 ...この場合、

BinSTree.cpp:156: error: no matching function for call to ‘TreeNode<int>::PostOrderVisit(TreeNode<int>*, void (BinSTree<int>::*)(const int&))’ 
TreeNode.cpp:56: note: candidates are: static void TreeNode<T>::PostOrderVisit(TreeNode<T>*, void (*)(const T&)) [with T = int] 

を言う++ gが、私はvoid (BinSTree<T>::*)(const T&)void (*)(const T&)のインスタンスだろうと思ったが、そうではありません。私は、呼び出しは関数定義によって認識されるように得ることができる唯一の方法は、このような関数ポインタをキャストすることによって、次のとおりです。

TreeNode<T>::PostOrderVisit(this->root(), (void (*)(const T& v)) &BinSTree<T>::Delete); 

これは、関数を認識し、適切にそれを呼び出します(ただし、これにはいくつかの重要な研究をしました... )、C++のメンバ関数には暗黙的なパラメータがあり、 'this'キーワードを内部からアクセスすることができます。メンバ関数ポインタをプレーン関数ポインタにキャストすると 'this'参照が完全に削除され、Delete()メソッドがsegフォルト(これはかなり 'これ'を使用します)の原因になります。

これは面倒なことであり、私はこのプロジェクトのこのような小さな部分にかなりの時間を費やしてきました。誰でも私に、A:キャスティングなしで機能を認識させる方法、B:キャスト全体で 'this'リファレンスを維持する方法を教えてください。 ClearTree()メソッドとDelete()メソッドは、どちらも同じクラス内にあります。

ありがとうございます。

+2

すでにテンプレートを使用している場合は、関数ポインタをスキップしてファンクタに直接アクセスしてください。非仮想関数ポインタでないポインタをインライン化することができます。 'visit'を' operator()() 'を多重定義できるテンプレートにします。 – asveikau

答えて

1

非静的メソッドは "this"の暗黙のパラメータをとります。例えば。メソッドC :: f(int i)では、f(C * this、int i)のように考えることができます。あなたがして、この署名をねじ込むあなたは、悪いことが起こると期待することができますキャスティング。あなたは既にクラッシュを経験していますが、もっと不吉な人工物は、プログラムを誤っているか、他の一見ランダムな場所でクラッシュさせる可能性があります。

あなたは、このようにメンバ関数へのポインタを使用することができます。

の.h

template <class C> 
static void PostOrderVisit(C* node, void (C::* visit)(const T& v)); 

に.cppファイルで(それはそれはすべての時間にしておく必要があり、テンプレート、そうでない場合はリンクエラーで実際にあれば)

template <class T> 
template <class C> 
void TreeNode<T>::PostOrderVisit(C* node, void (C::* visit)(const T& v)) 
{ 
    // ... 
    T value; 
    (node->*visit)(value); 
    // ... 
} 

あなたは、基本クラス(元のようにツリーノード)に、あなたの派生クラス(ここでのようにC)へのポインタまたはポインタを渡すのいずれか。ある時点でキャストする必要があるかもしれません。

訪問者として通常の機能を渡すときに元の機能を残すこともできます。関数のオーバーロードが注意を払うでしょう。

より一般的な方法は、std :: functionを使用することです。マイナーなパフォーマンスヒットがあるかもしれませんが、最も一般的です。

(コンパイルされていないいくつかのマイナーな構文エラーを有していてもよい):

static void PostOrderVisit(TreeNode<T>* node, std::function<void (const T& v)> visit); 

インサイドPostOrderVisitあなただけの、例えば、(値)をご覧ください通常の機能のように呼び出します。

PostOrderVisitを呼び出すと、std :: bindまたはboost :: bindのすべての機能を使用して、必要なだけ追加情報を保持できます。例えば。

PostOrderVisit(this-> root()、std :: bind(& BinSTree :: Delete、this));

3

まず、PostOrderVisitは、関数の引数をポインタとして使用する必要があります。つまり、PostOrderVisit(TreeNode<T>* node, void (*visit)(const T& v))です。

ただし、非静的メンバー関数を渡しているため、問題は解決しません。クラスに渡す関数がクラス内でstaticであるか、関数ポインタの引数の代わりにstd::functionのようなものを使用できます。つまり、PostOrderVisit(TreeNode<T>* node, std::function<void(const T&)> visit)です。

編集 その場合、私はあなたがこれを行うことができる2つの方法があると思う:一つは、あなたがパラメータとしてメンバメソッドを使用することができないことを意味し、パラメータに合わせてデザインを変更することですが。 2つ目は、デザインに合うようにコードを変更し、その制限のためにインターフェイスを変更する必要があることを教師に説明し、その制限について説明します。

引数として通常の関数ポインタを使用する際の問題は、クラスのインスタンスに対して、メンバ関数が暗黙的で隠された引数thisを持つことです。通常の関数はこの隠しパラメータを持たないため、コンパイラはメンバ関数の使用を禁止します。解決策は、C++ではない通常の関数を使うか、staticメンバ関数(thisポインタを持たない)を使うか、std::functionのようなものを使うかのどちらかです。

std::functionの使い方は、私が示したようにPostOrderVisitの宣言と定義で使用します。あなたがこれを呼び出すと、次のようなことが起こります:

template <class T> void BinSTree<T>::ClearTree() { 
    TreeNode<T>::PostOrderVisit(this->root(), std::mem_fn(&BinSTree<T>::Delete)); 
} 
+0

PostOrderVisitのパラメータを変更するにはどのくらいの道を借りているのかはっきりしていません。関数を呼び出す場所でstd :: functionを使用できますか? PostOrderVisitの宣言に含まれていなければなりませんか?申し訳ありませんが、私はまだC++に少し新しかったです。 –

+0

@JayElrod私の答えにいくつかの説明を追加しました。 –