3

まず第一に、私は C++ にかなり慣れていないので、これが初心者のコーディング ミスである場合は申し訳ありません。

私は現在、学校で受けた宿題のグラフ作成クラスに取り組んでいます。セット、配列、およびリンクされたリストにエッジを格納できるはずです。これらすべてを個別のクラスで行ったので、テンプレートを使用してすべてを 1 つにまとめようとしています。つまり、すべてが正常に機能します。std::set ですが、独自のリンク リストの実装を使用すると、どういうわけか失敗します。イテレータがどこかで台無しになり、前置演算子と後置演算子の両方が (for ループで) 同じ動作になるようです。また、リンクされたリストの独自の実装を作成することになっているため、std::list を使用していないことも付け加えておきます。

イテレータの現在の実装:

template<typename T>
class Iterator{
    public: node<T>* pointer;
    public:
        Iterator(): pointer(0){}
        Iterator(node<T>* _pointer): pointer(_pointer){}

        Iterator<T> operator++()    { pointer = pointer->next; }
        Iterator<T> operator++(int) { pointer = pointer->next; }

        bool operator!=(Iterator<T> rval){ return !(pointer == rval.pointer); }
        bool operator==(Iterator<T> rval){ return (pointer == rval.pointer); }

        node<T>* operator()(){ return pointer; }

        T operator*(){ return pointer->data; }

};

単一の連結リスト ノード:

template <typename T>
struct node{
    node(): next(0){}
    node(T val): data(val), next(0){}
    node(node<T>* _next): data(0), next(_next){}
    node(T val, node<T>* _next): data(val), next(_next){}

    T data;
    node<T>* next;
};

そして、リスト クラスが begin() と end() を実装する方法:

typedef Iterator<T> iterator;
iterator  begin()   { return iterator(new node<T>(b)); }
iterator  end()     { return iterator(); } 

bリンクされたリストの最初の要素を指していることに注意してください

最後に、要素にアクセスする方法 (これは、リストを含む別のクラスにあります):

void tree_recurse_f(int node, std::ofstream* file, int level = 0){
   [some output code here]
   typename T::iterator it;
   for (it = Database[node].first.begin(); it != Database[node].first.end(); ++it){
      tree_recurse_f(*it, file, (level+1));
   }
}

Database(セット、リスト、またはベクトル) で指定された型std::map<int,std::pair<>>.first指すandです。T

今問題に:

  1. どういうわけか、リストの現在の実装ではbegin()、出力関数の空のノードを指しています (++it と it++ は同じ結果になります)。
  2. begin()を to に変更するreturn iterator(b)と、for ループのエラーが取り除かれるように見えますが、++it と it++ はどちらも同じ結果になります。
  3. リストクラスのみをテストすることで、これら2つのエラーを発見することができました-グラフクラスに実装すると、出力関数で終わりのないループに入ります(*常に0を指し、+で増加しないようです) +それ)

私にはイテレータの奇妙なもののように見えます(特に、単独で機能するが、他のクラス内では機能しないという事実)

// 誰かが興味を持っているなら、私はhttp://www.cplusplus.com/articles/Lw6AC542/のリンクされたリストのチュートリアルに大まかに従っています

4

3 に答える 3

7

前置演算子と後置演算子は、同じことを行うように定義したため、同じことを行います。

Iterator<T> operator++()    { pointer = pointer->next; }
Iterator<T> operator++(int) { pointer = pointer->next; }

コードは同じですが、最も重要なのはUndefined Behaviorです。これは、関数が型の値を返すことになっていてIterator<T>、代わりに何も返さないためです。C++11 標準のパラグラフ 6.6.3/2 によると:

[...] 関数の最後を流れることは、値のない戻りと同等です。これにより、値を返す関数で未定義の動作が発生します。

プレフィックス イテレータを次のように変更する必要があります。

Iterator<T> operator++() 
{ 
    pointer = pointer->next; 
    return *this;
}

そして、後置イテレータを次のようにします。

Iterator<T> operator++(int) 
{ 
    node<T>* previous = pointer;
    pointer = pointer->next; 
    return Iterator<T>(previous);
}

また、あなたのデザインを正しく理解していれば、これを行うべきではないと思います:

iterator begin() { return iterator(new node<T>(b)); }

私はむしろやりたい:

iterator begin() { return iterator(b); }
于 2013-02-27T21:36:49.930 に答える
2

ここでの実装にはステートメントoperator++がありません。return戻り値は、プレインクリメントとポストインクリメントの主な違いです。pre-increment はイテレータを取り、それをインクリメントして、新しいイテレータ値を返します。元の反復子は戻り値と同じであるため、通常は参照によって戻ります。後置インクリメントはイテレータを取得し、それを削除し、元のイテレータをインクリメントして、保存されたコピーを返します。これは通常、適用されたイテレータとは異なるイテレータを返すため、値で返されます。したがって、これらに一致するように署名を変更します。

Iterator<T>& operator++();
Iterator<T> operator++(int);

私が説明したものと一致するように実装を変更します。

于 2013-02-27T21:37:29.537 に答える
0

前置演算子と後置++演算子は実際には何も返していません。あなたのコンパイラが文句を言っていないことに驚いています。

前置++演算子は単に を返す必要があり*thisます。後置++演算子は*this、変更前の一時コピーを作成し、そのコピーを返す必要があります。

于 2013-02-27T21:35:59.040 に答える