C ++ STL Set:找不到()最后插入的元素

时间:2010-08-10 20:42:34

标签: c++ stl

我正在编写一个应用程序,在该应用程序中我使用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()都有问题,我会接受有关如何使用堆分配的指针神奇地填充集合而不使用它们的建议。否则,评论“太多新的()调用”只会让你看起来很傻。

请关注正在发生的实际问题(现已解决)。感谢。

***修改

也许我应该把这些放在原来的问题中。但是我希望有更多关注find()的问题(或者因为它变得更像strcmp而不是更少的fileinfo_comparator函数),然后是复制粘贴PoC单元测试的代码审查。

以下是有关完整应用程序本身代码的一些要点。

  • FileInfo包含大量数据和文件名。它包含SHA1总和,文件大小,模态时间,最后编辑时的系统状态等。我已经删除了这篇文章的代码必须。它违反了3的规则(感谢@Martin York。请参阅wiki链接的评论)。
  • 最初选择使用char * over std :: string是因为使用了接受char *的3rd_party API。该应用程序从那时起逐渐发展。更改此选项不是一种选择。
  • FileInfo中的数据从系统上的命名管道进行轮询,并存储在Singleton中以便跨多个线程进行访问。 (如果我没有在堆上分配,我会有范围问题)
  • 我选择在Set中存储指针,因为FileInfo对象很大并且不断地从Set中添加/删除。我认为指针比将大型结构复制到Set中更好。
  • 我的析构函数中的if语句是不必要的,并且是我正在跟踪的问题的调试中的遗留物。它应该被拔出,因为它是不需要的。

3 个答案:

答案 0 :(得分:10)

您的比较函数错误 - 它返回bool,而不是整数strcmp(3)。 return语句应该是这样的:

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

看看here

另外,出于好奇,为什么不使用std::set<std::string>呢? STL实际上具有良好的默认值,并使您免于大量的手动内存管理。

答案 1 :(得分:2)

我认为FileInfo无法正常工作(至少在std::set中使用)。要存储在std::set中,比较函数应返回bool,表示这两个参数按顺序排列(true)或按顺序排列(false)。< / p>

鉴于你的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"));
 }

答案 2 :(得分:1)

在你的构造函数中:

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

m_fileName似乎未初始化。