1

現在、データ構造クラス用のテンプレート化されたバイナリ検索ツリーを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);
}

そしてここに問題があります。g++は言う...

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]

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

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

これは関数を認識して適切に呼び出しますが(これにはかなりの調査が必要です...)、C ++メンバー関数には、「this」キーワードに内部からアクセスできるようにする暗黙のパラメーターがあります。メンバー関数ポインターをプレーン関数ポインターにキャストすると、「this」参照が完全に削除され、Delete()メソッドが障害をセグメント化する原因になります(「this」をかなり使用します)。

これは面倒なことでした、そして私はこのプロジェクトのそのような小さなビットにかなりの時間を費やしました。誰かが私にA:キャストなしで関数を認識させる方法、またはB:キャスト全体で「this」参照を維持する方法を教えてもらえますか?ClearTree()メソッドとDelete()メソッドはどちらも同じクラス内にあります。

前もって感謝します。

4

2 に答える 2

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つの方法があると思います。1つは、パラメーターに合わせてデザインを変更することです。つまり、メンバーメソッドをパラメーターとして使用することはできません。2つ目は、デザインに合わせてコードを変更し、その制限のためにインターフェースを変更する必要があることを教師に説明し、それらの制限を説明することです。

通常の関数ポインタを引数として使用する場合の問題は、メンバー関数がthisクラスのインスタンスに対して暗黙的で非表示の引数を持っていることです。通常の関数にはこの隠しパラメーターがないため、コンパイラーはメンバー関数の使用を禁止しています。解決策は、C ++風ではない通常の関数を使用するか、メンバー関数(ポインターstaticがないため)を使用するか、のようなものを使用することです。thisstd::function

使い方std::functionは、先ほどの宣言や定義で使っていますPostOrderVisit。あなたがそれを呼ぶとき、あなたはこのようなことをします:

template <class T> void BinSTree<T>::ClearTree() {
    TreeNode<T>::PostOrderVisit(this->root(), std::mem_fn(&BinSTree<T>::Delete));
}
于 2012-04-10T07:02:27.933 に答える
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内(実際には、テンプレートの場合はすべてh内にある必要があり、そうでない場合はリンクエラー)

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

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

ビジターとして通常の機能を渡す場合に備えて、元の機能を残すこともできます。関数のオーバーロードが処理されます。

より一般的な方法は、std::functionを使用することです。パフォーマンスに多少の影響があるかもしれませんが、最も一般的です。

例(コンパイルされていない場合、いくつかのマイナーな構文エラーがある可能性があります):

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

PostOrderVisit内では、visit(value)を実行するだけです。たとえば、通常の関数のように呼び出します。

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

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

于 2012-04-10T07:31:01.213 に答える