C++ STL集:无法find()最后插入的元素
我正在编写一个应用程序,其中使用 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。请参阅维基链接的评论)。
- 最初选择使用 char* 而不是 std::string 是因为使用了接受 char* 的 3rd_party API。该应用程序从此开始发展。改变这一点不是一个选择。
- FileInfo 中的数据是从系统上的命名管道轮询的,并存储在 Singleton 中以便跨多个线程访问。 (如果我不在堆上分配,我会遇到范围问题)
- 我选择在 Set 中存储指针,因为 FileInfo 对象很大并且不断从 Set 中添加/删除。我认为指针比总是将大型结构复制到集合中更好。
- 我的析构函数中的 if 语句是不必要的,并且是调试我正在跟踪的问题时遗留下来的工件。应该将其拔出,因为不需要。
I am in the process of writing an application in which I use the Set class in the C++ STL. I've discovered that the call to set->find() always seems to fail when I query for the last element I inserted. However, if I iterate over the set, I am able to see the element I was originally querying for.
To try to get a grasp on what is going wrong, I've created a sample application that exhibits the same behavior that I am seeing. My test code is posted below.
For the actual application itself, I need to store pointers to objects in the set. Is this what is causing the weird behavior. Or is there an operator I need to overload in the class I am storing the pointer of?
Any help would be appreciated.
#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() );
}
}
The Output I get from this is:
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]
**Edit
It's kind of sad that I have to add this. But as I mentioned before, this is a sample application that was pulled from different parts of a larger application to exhibit the failure I was receiving.
It is meant as a unit test for calling set::find on a set populated with heap allocated pointers. If you have a problem with all the new()s, I'm open to suggestions on how to magically populate a set with heap allocated pointers without using them. Otherwise commenting on "too many new() calls" will just make you look silly.
Please focus on the actual problem that was occurring (which is now solved). Thanks.
***Edit
Perhaps I should have put these in my original question. But I was hoping there would be more focus on the problem with the find() (or as it turns out fileinfo_comparator function that acts more like strcmp than less), then a code review of a copy-paste PoC unit test.
Here are some points about the code in the full application itself.
- FileInfo holds a lot of data along with the filename. It holds SHA1 sums, file size, mod time, system state at last edit, among other things. I have cut out must of it's code for this post. It violates the Rule of 3 in this form (Thanks @Martin York. See comments for wiki link).
- The use of char* over std::string was originally chosen because of the use of 3rd_party APIs that accept char*. The app has since evolved from then. Changing this is not an option.
- The data inside FileInfo is polled from a named pipe on the system and is stored in a Singleton for access across many threads. (I would have scope issues if I didn't allocate on heap)
- I chose to store pointers in the Set because the FileInfo objects are large and constantly being added/removed from the Set. I decided pointers would be better than always copying large structures into the Set.
- The if statement in my destructor is needless and a left over artifact from debugging of an issue I was tracking down. It should be pulled out because it is unneeded.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
您的比较函数是错误的 - 它返回
bool
,而不是strcmp(3)
那样的整数。 return 语句应该类似于:看看这里。
另外,出于好奇,为什么不直接使用
std::set
来代替呢? STL 实际上有不错的默认设置,可以让您免于大量手动内存管理。Your comparison function is wrong - it returns
bool
, not integer asstrcmp(3)
. The return statement should be something like:Take a look here.
Also, out of curiosity, why not just use
std::set<std::string>
instead? STL actually has decent defaults and frees you from a lot of manual memory management.在我看来,您的
FileInfo
无法正常工作(至少在std::set
中使用)。要存储在std::set
中,比较函数应返回一个bool
指示两个参数按顺序 (true
) 或乱序(false
)。考虑到您的
FileInfo
的作用(对std::string
的设计不当的模仿),完全没有它可能会更好。据我所知,您可以使用std::string
代替它,而不会丢失任何功能。您还无缘无故地使用了大量动态分配(并且泄漏了您分配的大量内容)。编辑:即使(或者实际上,尤其是)
FileInfo
更复杂,它也不应该尝试自行重新实现字符串功能。它仍应使用std::string
作为文件名,并实现与之配合使用的operator<
:It looks to me like your
FileInfo
doesn't work correctly (at least for use in astd::set
). To be stored in astd::set
, the comparison function should return abool
indicating that the two parameters are in order (true
) or out of order (false
).Given what your
FileInfo
does (badly designed imitation ofstd::string
), you'd probably be better off without it completely. As far as I can see, you can usestd::string
in its place without any loss of functionality. You're also using a lot of dynamic allocation for no good reason (and leaking a lot of what you allocate).Edit: even when (or really, especially when)
FileInfo
is more complex, it shouldn't attempt to re-implement string functionality on its own. It should still use anstd::string
for the file name, and implement anoperator<
that works with that:在您的构造函数中:
m_fileName
似乎未初始化。In your constructor:
m_fileName
seems to be not initialized.