C ++ STL Set: Не удается найти () последний вставленный элемент - PullRequest
1 голос
/ 11 августа 2010

Я нахожусь в процессе написания приложения, в котором я использую класс Set в C ++ STL.Я обнаружил, что вызов 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 () (или, как выясняется, функция fileinfo_comparator, которая действует больше как strcmp, чем less), чем к проверке кода модульного теста PoC копирования-вставки.

Вот несколько моментов, касающихся кода в самом полном приложении.

  • FileInfo содержит много данных вместе с именем файла.Он содержит суммы SHA1, размер файла, время модификации, состояние системы при последнем редактировании, среди прочего.Я вырезал код из этого поста.Он нарушает Правило 3 в этой форме (Спасибо @Martin York. См. Комментарии к вики-ссылке).
  • Использование char * over std :: string изначально было выбрано из-за использования API-интерфейсов 3rd_party, которые принимаютсимвол *.С тех пор приложение развивалось.Изменить это не вариант.
  • Данные внутри FileInfo опрашиваются из именованного канала в системе и хранятся в Singleton для доступа ко многим потокам.(У меня возникли бы проблемы с областью действия, если бы я не выделял в куче)
  • Я решил хранить указатели в наборе, поскольку объекты FileInfo большие и постоянно добавляются / удаляются из набора.Я решил, что указатели будут лучше, чем всегда копировать большие структуры в Set.
  • Оператор if в моем деструкторе не нужен и является артефактом, оставшимся после отладки проблемы, которую я отслеживал.Его нужно вытащить, потому что он не нужен.

Ответы [ 3 ]

10 голосов
/ 11 августа 2010

Ваша функция сравнения неверна - она ​​возвращает bool, а не целое число, как strcmp(3).Оператор return должен выглядеть примерно так:

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

Взгляните здесь .

Кроме того, из любопытства, почему бы просто не использовать std::set<std::string> вместо этого?STL на самом деле имеет достойные значения по умолчанию и освобождает вас от большого количества ручного управления памятью.

2 голосов
/ 11 августа 2010

Мне кажется, что ваш 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"));
 }
1 голос
/ 11 августа 2010

В вашем конструкторе:

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

m_fileName кажется не инициализированным.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...