web-dev-qa-db-ja.com

ベクトルを繰り返し、特定のアイテムを削除します

Std :: vector m_vPathsがあります。このベクターを反復処理し、:: DeleteFile(strPath)を呼び出します。ファイルを正常に削除したら、ベクターから削除します。私の質問は、2つのベクトルを使用しなければならないのですか?私がする必要があるものにより適しているかもしれない異なるデータ構造がありますか?

例:反復子を使用すると、ほぼ希望どおりに動作しますが、反復子を使用して消去すると、すべての反復子が無効になります。

 std::vector<std::string> iter = m_vPaths.begin();
    for( ; iter != m_vPaths.end(); iter++) {
        std::string strPath = *iter;
        if(::DeleteFile(strPath.c_str())) {
            m_vPaths.erase(iter);   
                //Now my interators are invalid because I used erase,
                //but I want to continue deleteing the files remaining in my vector.    
        }
    }

2つのベクトルを使用でき、問題は発生しなくなりますが、私がやろうとしていることを行うためのより良い、より効率的な方法はありますか?

ところで、不明確な場合、m_vPathsは次のように宣言されます(私のクラスで):

std::vector<std::string> m_vPaths;
62
cchampion

チェックアウト - std::remove_if

#include <algorithm> // for remove_if
#include <functional> // for unary_function

struct delete_file : public std::unary_function<const std::string&, bool> 
{
    bool operator()(const std::string& strPath) const
    {
        return ::DeleteFile(strPath.c_str());
    }
}

m_vPaths.erase(std::remove_if(m_vPaths.begin(), m_vPaths.end(), delete_file()),
                m_vPaths.end());

使う - std::list 無効な反復子の問題を停止するには、ランダムアクセスを失います。 (一般的にキャッシュパフォーマンス)


レコードの場合、コードを実装する方法は次のとおりです。

typedef std::vector<std::string> string_vector;
typedef std::vector<std::string>::iterator string_vector_iterator;

string_vector_iterator iter = m_vPaths.begin();
while (iter != m_vPaths.end())
{
    if(::DeleteFile(iter->c_str()))
    {
        // erase returns the new iterator
        iter = m_vPaths.erase(iter);
    }
    else
    {
        ++iter;
    }
}

ただし、 std::remove_if (車輪の再発明は悪い)。

75
GManNickG

erase() メソッドは、削除された要素の後の次の要素を指す新しい(有効な)イテレータを返します。この反復子を使用して、ループを続行できます。

std::vector<std::string>::iterator iter;
for (iter = m_vPaths.begin(); iter != m_vPaths.end(); ) {
    if (::DeleteFile(iter->c_str()))
        iter = m_vPaths.erase(iter);
    else
        ++iter;
}
117
sth

ファイルを消去する時間を考えると、おそらく重要ではありませんが、ベクターを逆方向に反復することをお勧めします-通常、ベクターの末尾から(近くの)アイテムを削除します。アイテムの削除にかかる時間は、ベクター内でそれに続くアイテムの数に比例します。 (たとえば)100個のファイル名のベクトルがあり、それらすべてを正常に削除した場合、プロセスの最後の要素を100回コピーします(2番目から最後の要素を99回コピーします)。

OTOH、最後から始めて逆方向に作業する場合、ファイルの削除が成功する限りコピーしません。逆イテレータを使用して、他の多くを変更せずにベクトルを逆方向​​にトラバースできます。たとえば、remove_ifを使用したGManのコードは、begin()をrbegin()に、endをrend()に置き換えるだけで、引き続き動作します(わずかに高速)。

別の可能性は、ベクターの代わりに両端キューを使用することです-両端キューは、末尾から項目を消去できますまたは一定の時間でコレクションの先頭になります。

7
Jerry Coffin