C++ STL集:不能找到()最后一个元素插入 我正在编写一个应用程序,在其中我使用C++类中的SET类。我发现,当我查询插入的最后一个元素时,对set->find的调用似乎总是失败。但是,如果我在集合上迭代,我可以看到我最初查询的元素

C++ STL集:不能找到()最后一个元素插入 我正在编写一个应用程序,在其中我使用C++类中的SET类。我发现,当我查询插入的最后一个元素时,对set->find的调用似乎总是失败。但是,如果我在集合上迭代,我可以看到我最初查询的元素,c++,stl,C++,Stl,为了试图了解出了什么问题,我创建了一个示例应用程序,它展示了与我看到的相同的行为。我的测试代码发布在下面 对于实际应用程序本身,我需要存储指向集合中对象的指针。这就是导致这种奇怪行为的原因。或者在我存储指针的类中是否有需要重载的运算符 任何帮助都将不胜感激 #include <stdio.h> #include <set> using namespace std; #define MySet set<FileInfo *,bool(*)(const FileIn

为了试图了解出了什么问题,我创建了一个示例应用程序,它展示了与我看到的相同的行为。我的测试代码发布在下面

对于实际应用程序本身,我需要存储指向集合中对象的指针。这就是导致这种奇怪行为的原因。或者在我存储指针的类中是否有需要重载的运算符

任何帮助都将不胜感激

#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() );
    }
}
**编辑 我不得不补充这一点,这有点令人难过。但正如我前面提到的,这是一个示例应用程序,它是从一个更大的应用程序的不同部分提取的,以显示我收到的失败

它的意思是作为一个单元测试,用于在由堆分配指针填充的集合上调用set::find。如果您对所有的新闻都有疑问,我愿意接受关于如何神奇地使用堆分配的指针填充集合而不使用它们的建议。否则,评论太多的新电话只会让你看起来很傻

请关注正在发生的实际问题,该问题现已解决。谢谢

***编辑

也许我应该把这些放在我最初的问题中。但我希望find or的问题会得到更多关注,因为事实证明fileinfo_comparator函数更像strcmp而不是less,然后是对复制粘贴PoC单元测试的代码审查

下面是关于完整应用程序本身中的代码的一些要点

FileInfo与文件名一起保存大量数据。它包含SHA1总和、文件大小、修改时间、最后编辑时的系统状态等。我删掉了这篇文章的代码。这违反了3条规则,谢谢@Martin York。请参阅wiki链接的注释。 最初选择在std::string上使用char*是因为使用了接受char*的第三方API。从那时起,该应用程序就开始发展。改变这一点不是一个选项。 FileInfo中的数据从系统上的命名管道轮询,并存储在一个单例中,以便跨多个线程访问。如果我没有在堆上分配,我会有范围问题 我选择在集合中存储指针,因为FileInfo对象很大,并且不断地从集合中添加/删除。我认为指针比总是将大型结构复制到集合中要好。 我的析构函数中的if语句是不必要的,是调试我跟踪的问题时遗留下来的工件。它应该被拔出来,因为它是不需要的。
您的比较函数是错误的-它返回bool,而不是strcp3那样的整数。return语句应该类似于:

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


另外,出于好奇,为什么不直接使用std::set呢?STL实际上有相当好的默认值,使您从大量手动内存管理中解放出来。

您的比较函数是错误的-它返回bool,而不是strcp3那样的整数。return语句应该类似于:

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

另外,出于好奇,为什么不直接使用std::set呢?STL实际上有相当好的默认值,并将您从大量手动内存管理中解放出来。

在您的构造函数中:

FileInfo( const FileInfo & file ) 
        { 
            setFile( file.getFile() ); 
        }
m_文件名似乎未初始化。

在构造函数中:

FileInfo( const FileInfo & file ) 
        { 
            setFile( file.getFile() ); 
        }
m_文件名似乎未初始化。

在我看来,您的文件信息至少在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::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;
}

请看一看typedef关键字-不需要使用宏来缩短类型名。不相关的挑剔:删除指针之前不需要检查它们是否为null。删除空指针是安全的。为什么要在任何地方使用new everywhere?这不是Java。@Martin哇,我不认为我需要删除空指针
解释什么是示例应用程序。实际代码分布在许多类中。也许所有的新闻看起来都很有趣,但这不是一个真正的应用程序。当项目被分配到需要它们的堆上时,问题就表现出来了。然后,一个有效的样本或测试应用程序需要分配一些东西……准备好了吗。。。。在堆上!我的问题显然不是堆与堆栈的分配问题。但是谢谢你空洞的评论。@AngryLuke:不,你在阅读代码时遇到的问题源于对语言的理解不足。如图所示,以下是正确答案。事实上,您到处都在使用new,没有进行适当的内存管理,没有使用正确的类,没有理解delete是如何工作的,没有理解delete规则3或4,没有理解可以使用指向堆对象的指针,没有理解C-String是无意义的。如果你学会正确使用语言,那么这样的问题就不会发生。这就是为什么我指出了不正确的Java样式。请看一下typedef关键字-没有必要使用宏来缩短类型名。不相关的挑剔:删除指针之前不需要检查null。删除空指针是安全的。为什么在任何地方都使用new everywhere?这不是Java。@Martin Wow,我想我不需要解释什么是示例应用程序。实际代码分布在许多类中。也许所有的新闻看起来都很有趣,但这不是一个真正的应用程序。当项目被分配到需要它们的堆上时,问题就表现出来了。然后,一个有效的样本或测试应用程序需要分配一些东西……准备好了吗。。。。在堆上!我的问题显然不是堆与堆栈的分配问题。但是谢谢你空洞的评论。@AngryLuke:不,你在阅读代码时遇到的问题源于对语言的理解不足。如图所示,以下是正确答案。事实上,您到处都在使用new,没有进行适当的内存管理,没有使用正确的类,没有理解delete是如何工作的,没有理解delete规则3或4,没有理解可以使用指向堆对象的指针,没有理解C-String是无意义的。如果你学会正确使用语言,那么这样的问题就不会发生。这就是为什么我指出了错误的java风格。+ 1:新的溃烂是C++应用程序中编码错误的标志。谢谢@Metthieu请查看什么是概念验证应用程序。当读取从更大的应用程序中提取的代码以重现问题时,您可能无法完全理解这些代码。一个好的程序员知道PoC代码可能看起来很有趣,但可以忽略它来发现真正的问题。这还不足以使fileinfo\u comparator正确。比较需要执行严格的弱排序。当任一值为NULL时,都不会发生这种情况。正如上面所说的,在上面的测试集中没有空值。它是不会发生故障的。+ 1:新的溃烂是C++应用程序中编码错误的标志。谢谢@Metthieu请查看什么是概念验证应用程序。当读取从更大的应用程序中提取的代码以重现问题时,您可能无法完全理解这些代码。一个好的程序员知道PoC代码可能看起来很有趣,但可以忽略它来发现真正的问题。这还不足以使fileinfo\u comparator正确。比较需要执行严格的弱排序。当任一值为NULL时,都不会发生这种情况。碰巧上面没有失败,因为上面的测试集中没有空值。谢谢你的评论。FileInfo的真实形式实际上要大得多,它保存着关于文件总数、大小、修改时间、修改时的系统状态等的数据。我删减了它,因为所有额外的东西都与我的问题无关。谢谢你的评论。FileInfo的真实形式实际上要大得多,它保存着关于文件总数、大小、修改时间、修改时的系统状态等的数据。我把它删减了,因为所有额外的东西都与我的问题无关。