通过 returns front() 和 pops() 的函数清空 std::queue

emptying an std::queue through a function that returns the front() and pops()

我正在编写一个模板 class,它应该提供一个队列来防止并发访问,基本上是为了能够编写 classes 来处理带有变量的项目列表并发工作线程数。

模板class有一个方法:

bool getFront(T &value)
{ CritSectEx::Scope scope(listLock);
  bool ret = false;
    if( !itemList.empty() ){
        value = itemList.front();
        itemList.pop();
        ret = true;
    }
    return ret;
}

锁定队列,如果它不为空,则获取第一个元素,将其弹出并returns为真,这样每个工作人员都可以做

    while( getFront(entry) ){
        // do something with entry
        nTreated += 1;
    }
    // exit worker

问题是:我通过 value 参考返回的究竟是什么?当我测试这段代码时,我得到一个双重释放错误,因为我正在使用的类型 T 包含一个指针(我们称之为 B),该指针在其 dtor 中被删除(当 B 未标记为指向 a 的指针时全局结构)。在 getFront() 中的 pop() 期间调用了 dtor,这似乎合乎逻辑。我注意类型 T 的 ctors 在从现有实例创建新 T 时都会分配 B 的副本,并且它不会 subclass 任何可以提供 '=' 运算符的东西,所以我不确定我如何最终会得到 2 个 T 实例,每个实例都包含相同的 B 值和 "we own B" 成员。

我做这种事情的次数不够多,所以我可能忽略了这里的一些东西,但是什么?有任何解决此问题的建议吗?

额外观察:似乎总是从队列中弹出的最后一个元素显示此行为。

为了使这不那么模糊,这是我用于类型名 T 的内容:

struct folder_info
{
    // plenty of members snipped
#ifdef __cplusplus
public:
    folder_info()
    {
        memset( this, 0, sizeof(struct folder_info) );
    }
    folder_info(const struct folder_info *src)
    {
        init(src);
    }
    folder_info(const struct folder_info &src)
    {
        init(&src);
    }
private:
    void init(const struct folder_info *src)
    {
        memcpy( this, src, sizeof(struct folder_info) );
        // we don't duplicate the filetypeslist!
        filetypeslist = NULL;
        filetypeslistlen = filetypeslistsize = 0;
    }
#endif
};

typedef struct FileEntry {
public:
    std::string fileName;
    struct stat fileInfo;
    FolderInfo *folderInfo;
    bool freeFolderInfo;
    FileEntry();
    FileEntry( const char *name, const struct stat *finfo, FolderInfo *dinfo, const bool ownInfo=false );
    FileEntry( const char *name, const struct stat *finfo, FolderInfo &dinfo );
    FileEntry(const FileEntry &ref);
    ~FileEntry();
} FileEntry;

FileEntry::FileEntry()
{
    folderInfo = NULL;
    freeFolderInfo = false;
}

FileEntry::FileEntry( const char *name, const struct stat *finfo, FolderInfo *dinfo, const bool ownInfo )
{
    fileName = name;
    fileInfo = *finfo;
    folderInfo = (ownInfo)? new FolderInfo(dinfo) : dinfo;
    freeFolderInfo = ownInfo;
}

FileEntry::FileEntry( const char *name, const struct stat *finfo, FolderInfo &dinfo )
{
    // creating a FileEntry with a FolderInfo reference always makes a copy
    // of the FolderInfo structure
    FileEntry( name, finfo, new FolderInfo(dinfo), true );
}

FileEntry::FileEntry(const FileEntry &ref)
{
    fileName = ref.fileName;
    fileInfo = ref.fileInfo;
    if( ref.freeFolderInfo ){
        folderInfo = new FolderInfo(ref.folderInfo);
    }
    else{
        folderInfo = ref.folderInfo;
    }
    freeFolderInfo = ref.freeFolderInfo;
}

FileEntry::~FileEntry()
{
    if( freeFolderInfo && folderInfo ){
        delete folderInfo;
        folderInfo = NULL;
    }
}

"The question is: what exactly am I returning here through the value reference?"

好吧,如果输出参数没有改变,调用者主要负责传入的值。虽然可能更好,但在对它做任何事情之前分配一个适当的默认值:

bool getFront(T &value)
{ 
  value = T(); // <<<<< Reset the output
//^^^^^^^^^^^^
  CritSectEx::Scope scope(listLock);
  bool ret = false;
    if( !itemList.empty() ){
        value = itemList.front();
        itemList.pop();
        ret = true;
    }
    return ret;
}

对于指针,这将导致 nullptr 设置为 value,这不会导致 delete 出现问题。

您的双重删除问题很可能是因为在调用 getFront().

之前输出值不正确 initialized/reset

一般来说,我建议使用 smart pointers 而不是原始指针,并尝试自己正确管理动态内存分配和删除。

使用类似于 std::unique_ptr<T> 的类型存储在您的队列中应该可以顺利解决您的问题。

万一我没弄错,问题是 FileEntry.

缺少用户定义的赋值运算符

如果复制的 FileEntryfreeFolderInfo == true,你的复制构造函数会处理每个 FileEntry 有一个新的 FolderInfo 的事实,而你的赋值运算符没有。

如果您将一个 FileEntry 分配给另一个,并且如果两者都有 freeFolderInfo == true,您将删除同一个指针两次,一旦两者中的最后一个超出范围 / 是已删除。

What is The Rule of Three?

If you need to explicitly declare either the destructor, copy constructor or copy assignment operator yourself, you probably need to explicitly declare all three of them.