簡體   English   中英

C ++ STL集:找不到最后插入的元素

[英]C++ STL Set: Cannot find() last element inserted

我正在編寫一個使用C ++ STL中的Set類的應用程序。 我發現查詢我插入的最后一個元素時,對set-> find()的調用似乎總是失敗。 但是,如果我遍歷該集合,則可以看到我最初查詢的元素。

為了嘗試了解發生了什么問題,我創建了一個示例應用程序,該應用程序表現出與我所看到的相同的行為。 我的測試代碼發布在下面。

對於實際的應用程序本身,我需要在集合中存儲指向對象的指針。 這是導致奇怪行為的原因。 還是在存儲指針的類中需要重載的運算符?

任何幫助,將不勝感激。

#include <stdio.h>
#include <set>

using namespace std;

#define MySet set<FileInfo *,bool(*)(const FileInfo *, const FileInfo*)>

class FileInfo
{
    public:
        FileInfo()
        {
            m_fileName = 0;
        }
        FileInfo( const FileInfo & file )
        {
            setFile( file.getFile() );
        }
        ~FileInfo()
        {
            if( m_fileName )
            {
                delete m_fileName;
                m_fileName = 0;
            }
        }
        void setFile( const char * file )
        {
            if( m_fileName )
            {
                delete m_fileName;
            }
            m_fileName = new char[ strlen( file ) + 1 ];
            strcpy( m_fileName, file );
        }
        const char * getFile() const
        {
            return m_fileName;
        }
    private:
        char * m_fileName;
};

bool fileinfo_comparator( const FileInfo * f1, const FileInfo* f2 )
{
    if( f1 && ! f2 ) return -1;
    if( !f1 && f2 ) return 1;
    if( !f1 && !f2 ) return 0;

    return strcmp( f1->getFile(), f2->getFile() );
}

void find( MySet *s, FileInfo * value )
{
    MySet::iterator iter = s->find( value );
    if( iter != s->end() )
    {
        printf( "Found File[%s] at Item[%p]\n", (*iter)->getFile(), *iter );
    }
    else
    {
        printf( "No Item found for File[%s]\n", value->getFile() );
    }
}

int main()
{
    MySet *theSet = new MySet(fileinfo_comparator);

    FileInfo * profile = new FileInfo();
    FileInfo * shell = new FileInfo();
    FileInfo * mail = new FileInfo();

    profile->setFile( "/export/home/lm/profile" );
    shell->setFile( "/export/home/lm/shell" );
    mail->setFile( "/export/home/lm/mail" );

    theSet->insert( profile );
    theSet->insert( shell );
    theSet->insert( mail );

    find( theSet, profile );

    FileInfo * newProfile = new FileInfo( *profile );

    find( theSet, newProfile );

    FileInfo * newMail = new FileInfo( *mail );

    find( theSet, newMail );

    printf( "\nDisplaying Contents of Set:\n" );
    for( MySet::iterator iter = theSet->begin();
            iter != theSet->end(); ++iter )
    {
        printf( "Item [%p] - File [%s]\n", *iter, (*iter)->getFile() );
    }
}

我從中得到的輸出是:

Found File[/export/home/lm/profile] at Item[2d458]
Found File[/export/home/lm/profile] at Item[2d458]
No Item found for File[/export/home/lm/mail]

Displaying Contents of Set:
Item [2d478] - File [/export/home/lm/mail]
Item [2d468] - File [/export/home/lm/shell]
Item [2d458] - File [/export/home/lm/profile]

**編輯我不得不添加此內容,這有點令人遺憾。 但是,正如我之前提到的,這是一個示例應用程序,該應用程序是從較大應用程序的不同部分提取的,顯示出我收到的故障。

它旨在作為對填充了堆分配的指針的集合上的set :: find進行調用的單元測試。 如果您對所有new()都有疑問,我歡迎提出有關如何在不使用堆分配指針的情況下神奇地填充集合的建議。 否則,對“太多new()調用”進行評論只會使您看起來很傻。

請關注正在發生的實際問題(現已解決)。 謝謝。

***編輯

也許我應該把這些放在我最初的問題中。 但是我希望find()可以解決這個問題(或者結果證明fileinfo_comparator函數的行為更像是strcmp而不是更少),然后是復制粘貼PoC單元測試的代碼審查。

以下是有關完整應用程序本身中的代碼的一些要點。

  • FileInfo包含很多數據以及文件名。 它保存SHA1和,文件大小,修改時間,上次編輯時的系統狀態等。 我已經刪掉了這篇文章的代碼。 它違反了這種形式的3規則(感謝@Martin York。有關Wiki鏈接,請參見評論)。
  • 最初選擇在std :: string上使用char *,是因為使用了接受char *的3rd_party API。 從那時起,該應用程序得到了發展。 更改此選項不是一種選擇。
  • FileInfo內部的數據是從系統上的命名管道中輪詢的,並存儲在Singleton中以供跨多個線程訪問。 (如果沒有在堆上分配,我會遇到范圍問題)
  • 我之所以選擇將指針存儲在Set中是因為FileInfo對象很大,並且經常從Set中添加/刪除。 我認為指針比將大型結構復制到Set中要好。
  • 我的析構函數中的if語句是不必要的,並且是我正在跟蹤的問題的調試中遺留的工件。 應該將其拔出,因為它是不必要的。

您的比較函數是錯誤的-它返回bool ,而不是strcmp(3)整數。 return語句應類似於:

return strcmp( f1->getFile(), f2->getFile() ) < 0;

在這里看看。

另外,出於好奇,為什么不只使用std::set<std::string>呢? STL實際上具有不錯的默認設置,使您擺脫了很多手動內存管理。

在我看來,您的FileInfo不能正常工作(至少在std::set )。 要存儲在std::set ,比較函數應返回一個bool值,指示兩個參數的順序為( true )或順序為( false )。

鑒於FileInfo功能(對std::string不良設計模仿),如果沒有它,您可能會更好。 據我所知,您可以在其位置使用std::string而不損失任何功能。 您也無緣無故地使用了很多動態分配(並且泄漏了很多分配的資源)。

#include <set>
#include <iostream>
#include <iterator>
#include <string>

int main() { 
    char *inputs[] = { "/export/home/lm/profile", "/export/home/lm/shell", "/export/home/lm/mail" };
    char *outputs[] = {"Found: ", "Could **not** find: "};

    std::set<std::string> MySet(inputs, inputs+3);

    for (int i=0; i<3; i++)
        std::cout 
            << outputs[MySet.find(inputs[i]) == MySet.end()] 
            << inputs[i] << "\n";

    std::copy(MySet.begin(), MySet.end(), 
        std::ostream_iterator<std::string>(std::cout, "\n"));

    return 0;
}

編輯:即使(或實際上, 尤其是當FileInfo更復雜時,也不應嘗試自行重新實現字符串功能。 它仍應使用std::string作為文件名,並實現與之配合使用的operator<

class FileInfo { 
    std::string filename;
public:
    // ...
    bool operator<(FileInfo const &other) const { 
       return filename < other.filename;
    }
    FileInfo(char const *name) : filename(name) {}
};

std::ostream &operator(std::ostream &os, FileInfo const &fi) { 
    return os << fi.filename;
}

int main() { 
    // std::set<std::string> MySet(inputs, inputs+3);
    std:set<FileInfo> MySet(inputs, inputs+3);

    // ...

    std::copy(MySet.begin(), MySet.end(), 
        std::ostream_iterator<FileInfo>(std::cout, "\n"));
 }

在您的構造函數中:

FileInfo( const FileInfo & file ) 
        { 
            setFile( file.getFile() ); 
        }

m_fileName似乎未初始化。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM