Использование алгоритма STL для вектора указателей объекта (C ++) - PullRequest
1 голос
/ 06 марта 2012

Мне нужно посчитать, сколько раз объект массива указателей имеет то же имя (переменную-член), что и параметр, переданный функции-члену. Я пробовал разные подходы, но ни один из них не работал. Мой код даже не компилируется. Ошибка: «ошибка C2514:« MyComparator »: класс не имеет конструкторов», и вот код класса, который я использую для сравнения, и функция, используемая для подсчета совпадений.

 //definition of the vector
 vector<Class1*> files;
 ...
 int Class2::countNames(string name)
    {
       return count_if(files.begin(), files.end(), bind2nd(MyComparator(),name));
    }
 ...
class MyComparator{
public:
 bool operator()(const CFileType*& ob1, const string &str)
 {
   return ob1->getName()==str;
 }
};

Я борюсь с этим часами и хочу сделать это с помощью STL. Уловка здесь в том, что у меня есть вектор указателей, если бы у меня был нормальный вектор, ему не требовалась бы функция предиката, в моем случае я должен дать ей параметр, и я думаю, что bind2nd () - правильный путь. Любая помощь будет принята с благодарностью!

Ответы [ 4 ]

1 голос
/ 06 марта 2012

В принципе ваша идея работает, однако:

  1. класс компаратора должен быть определен до его использования;
  2. он должен наследоваться от binary_function, чтобы включить необходимые typedefs;
  3. его operator() необходимо объявить const.

С этими исправлениями у меня работает следующий пример:

#include <vector>
#include <functional>
#include <string>
#include <algorithm>
#include <iostream>

using namespace std;

struct Class1 {
  string getName() const { return "aaa"; }
};

//...
class MyComparator: public binary_function<const Class1*, string, bool> {
public:
 bool operator()(const Class1* ob1, const string &str) const
 {
   return ob1->getName()==str;
 }
};

vector<Class1*> files;
//...
 int countNames(string name)
    {
       return count_if(files.begin(), files.end(), bind2nd(MyComparator(),name));
    }

int main() {
  files.push_back(new Class1);
  files.push_back(new Class1);
  cout << countNames("aaa") << ' ' << countNames("bbb") << endl;
}

Обратите внимание, однако, что наличие вектора указателей легко приводит к утечкам памяти (как в моем примере). Попробуйте использовать Boost.PointerContainer или (с C ++ 11) контейнер unique_ptr s.

1 голос
/ 06 марта 2012

Прежде всего, вы должны убедиться, что ваш класс сравнения известен, когда вы его используете: перемещение моего определения MyComparator перед определением countNames() будет моим первым шагом. Тем не менее, bind2nd() хочет знать объекты-функции, с которыми он имеет дело, немного лучше, чем вы, что вы предлагаете: обычно оно хочет знать result_type, first_argument_type и second_argument_type. Вы можете получить их из std::binary_function<bool, CFileType const*, std::string const&> или явно указав их. Хотя я не думаю, что это требуется, вы можете захотеть сделать оператор вызова функции const. С другой стороны, если вы определяете объект функции, а не функцию (для которой вы могли бы получить необходимые typedef s, используя std::ptr_fun(); лично я думаю, что имя предназначено, чтобы каким-то образом сделать этих ребят привлекательными, потому что это, безусловно, не весело , когда вам нужно их использовать), вы можете просто пройти весь путь и вообще не вмешиваться со связующими:

class MyComparator {
public:
    MyComparator(std::string const& value): value_(value) {}
    bool operator()(CFileType const* obj) const {
        return obj->getName() == this->value_;
    }
private:
    std::string value_;
};
...
std::count_if(files.begin(), files.end(), MyComparator(name));

Вы можете получить примерно такой же эффект, используя связыватели при определении функции и ее связывании. Обычно я ошибаюсь, когда не пробую код, но он будет выглядеть примерно так:

bool myComparator(CFileType const* obj, std::string name) {
    return obj->getName() == name;
}
...
std::count_if(files.begin(), files.end(),
              std::bind2nd(std::ptr_fun(myComparator), name));

Если вы чувствуете, что хотите передать аргумент name по ссылке, а не копировать его постоянно, вы не сможете использовать std::bind2nd(), по крайней мере, если вы не используете компилятор C ++ 2011: он будет создайте тип, который должен состоять из последовательных ключевых слов const, что не понравится компилятору. Вы можете использовать, например, boost::bind(), у которого нет этой проблемы:

std::count_if(files.begin(), files.end(), boost::bind(myComparator, _1, name));

... и измените объявление параметра name на std::string const& name.

1 голос
/ 06 марта 2012

У вас есть несколько проблем здесь:

  • Похоже, вы определяете MyComparator после его использования. Типы должны быть определены перед использованием.
  • Вектор содержит Class1*, но компаратор работает с CFileType*.
  • Устаревшие bind1st и bind2nd немного сложны в использовании и требуют, чтобы различные типы определялись типом функции. Предполагая, что вы не можете использовать новый std::bind (или boost::bind), самый простой способ исправить это для MyComparator наследовать от std::binary_function<Class1*, string, bool>
  • operator() должен быть объявлен const.

Если ваш компилятор поддерживает C ++ 11, то вы можете написать это проще, используя std::bind или лямбду:

count_if(files.begin(), files.end(), bind(MyComparator(), placeholders::_1, name));

или

count_if(files.begin(), files.end(), [&name](Class1 const * p){return p->getName()==name;});
1 голос
/ 06 марта 2012

В C ++ 11 вы можете использовать лямбда-выражение, которое намного проще, чем архаичное bind2nd.Например:

#include <string>
#include <vector>
#include <algorithm>
#include <iostream>

class Class1 {
public:
    Class1(const std::string& name) : Name(name) {
    }
    const std::string& getName() const {
        return Name;
    }
private:
    std::string Name;
};

size_t CountMatchingNames(const std::vector<Class1*>& v, const std::string& name) {
    return std::count_if(
        v.begin(),
        v.end(),
        [&name](Class1* c1) { return name == c1->getName(); }
    );
}

int main() {

    Class1 rob("rob");
    Class1 bob("bob");
    Class1 mitch("mitch");

    std::vector<Class1*> v;
    v.push_back(&bob);
    v.push_back(&mitch);
    v.push_back(&rob);
    v.push_back(&mitch);
    v.push_back(&bob);
    v.push_back(&bob);

    std::cout << "rob count:\t" << CountMatchingNames(v, "rob") << std::endl;
    std::cout << "bob count:\t" << CountMatchingNames(v, "bob") << std::endl;
    std::cout << "mitch count:\t" << CountMatchingNames(v, "mitch") << std::endl;

    return EXIT_SUCCESS;

}

Это печатает:

rob count:      1
bob count:      3
mitch count:    2
...