Suche mittels find/find_if



  • Hi,

    folgende Aufgabenstellung:
    Ich habe eine Klasse CMyObj mit einem Member std::string m_Name.
    Weiterhin habe ich einen std::vector<CMyObj*>, in dem ich jetzt das CMyObj-Element finden möchte, dessen m_Name-Member mit einem vorgegebenen CString übereinstimmt. (Dieser vorgegebene String, gehört wieder zu einem Objekt einer anderen Klasse und steht erst zur Laufzeit fest).

    Da ich mit "find" hier nichts anfangen kann(?), wollte ich find_if verwenden, tue mir aber mit der Definition des "predicate" ein wenig schwer.

    Wenn ich es richtig verstehe, brauche ich als drittes Argument von find_if eine Funktion/einen Funktor, die/der in meinem Fall ein CMyObj* nimmt und ein bool zurückgibt.
    Wie ich dann den zu findenden String mit ins Spiel bringen soll, ist mir nicht ganz klar, da ich ihn der Funktion nicht zusätzlich übergeben kann und von innerhalb der Funktion keinen Zugriff habe...

    Folgend ein paar Code-Auszüge, die vielleicht die Aufgabe ein wenig verdeutlichen.

    // Ich habe einen vector "data", der mit Objekten einer struct gefüllt ist.
    // Ein Member dieser struct ist der zu suchende String
    std::vector<myStruct> data = //...    
    std::vector<myStruct>::iterator dataIter = data.begin();
    
    std::vector<CMyObj*>::iterator result;
    while(dataIter != data.end())
    {
        // Außerdem habe ich den zu durchsuchenden vector "m_myObjects", der mit CMyObj* gefüllt ist
        result = find_if(m_myObjects.begin(), m_myObjects.end(), /*???*/);
        // do something with result
        ++dataIter;
    }
    

    In jedem Schleifendurchlauf greife ich mir also ein Element aus dem vector "data" heraus. Das string-Member des aktuellen vector-Elements ist der string, nach dem ich suche.
    Ich durchsuche dann den vector m_myObjects nach diesem string, wobei er dort nicht direkt enthalten ist, sondern in Form eines Members der im vector enthaltenen Elemente.

    Anstelle von "???" sollte also oben etwas stehen wie:
    "Wennn folgende Bedingung erfüllt ist, ist das gesuchte Element gefunden":

    (*dataIter).m_StringToSearchFor == "aktuell betrachtetes Element von m_myObjects"->GetName()
    

    Ich hoffe, ich habe mein Problem einigermaßen verständlich erläutert. Würde mich über ein wenig Hilfestellung freuen.
    Vielen Dank im Voraus!



    1. Da du in deinem Container Pointer speicherst, sind einige Dinge anders als sie sonst wären (Toller Satz, was 😃 )
    2. Du kannst als "predicate" nicht einfach ne Memberfunktion übergeben die noch dazu noch Parameter erwartet, du musst schon nen passendes "function object" aka Funktor übergeben:
    class CMyObj
    {
    public:
        //...
        bool IsItsName(const std::string& name) const;
    };
    
    // unser Funktor:
    class CompareCMyObjName
    {
    public:
        CompareCMyObjName(std::string const& name)
            : m_name(name)
        {}
    
        // für ein "unary predicate" wie es find_if verlangt müssen wir einen
        // "bool operator ()(...)" definieren, der den Aufruf "f(*it)" ermöglicht,
        // wenn "f" die Instanz des Funktors ist und "it" der Iterator:
        bool operator () (CMyObj* p)
        {
            return p->IsItsName(m_name);
        }
    
    private:
        std::string m_name;
    };
    
    void foo()
    {
        std::vector<CMyObj*> v;
        // ... fill v ...
    
        // und so geht's dann:
        CompareCMyObjName pred("hans");
        std::find_if(v.begin(), v.end(), pred);
    
        // oder kürzer:
        std::find_if(v.begin(), v.end(), CompareCMyObjName("hans"));
    }
    


  • @hustbaer
    Jetzt habe ich an meinem ursprünglichen Beitrag eine ganze Weile herumeditiert, weil mir ein paar Dinge klar geworden sind und ich dachte, das schaut sich um diese Zeit ohnehin keiner mehr an. In der Zwischenzeit kam deine Antwort, die jetzt nicht mehr 100%ig zu meinem Beitrag passt. Sorry dafür!

    Trotzdem vielen Dank für die Antwort!
    Ich denke, ich habe es einigermaßen begriffen, werde es mir aber noch einmal in Ruhe anschauen müssen, bevor ich es ausprobiere.



  • Hmm, ich bin mir jetzt mit dem folgenden wie fast immer nicht ganz sicher:

    (std:: lass ich überall weg, fügs hinzu wo's sein muss, falls du kein using namespace std; nutzt)

    struct Comperator{    //sinnvolleren Namen einfallen lassen
        bool operator()(const CMyObj& obj,const string& s){   
            return obj.m_name==s;    //falls m_name private den struct hier als friend deklarieren
        }
    };
    
    while(dataIter != data.end())
    {
        result = find_if(m_myObjects.begin(), m_myObjects.end(),bind2nd(Comperator(),dataIter->m_StringToSearchFor)); //Unsicherheit bez. der Klammern bei Comperator
        // do something with result
        ++dataIter;
    }
    

    Vielleicht funktionniert es. Ich hätts so probiert zumindest. Kannst es ja mal versuchen.



  • Hier 😉 Anschauen und verstehen 🙂

    #include <iostream>
    #include <algorithm>
    #include <string>
    #include <vector>
    
    using namespace std;
    
    class MyClass {
    
               string name;
       public: 
               MyClass(const string& n) : name(n) { }
               string getName() const { return name; }
    };
    
    struct MyStruct {
           string stringToSearch;
           MyStruct(const string& n) : stringToSearch(n) { }
    };
    
    bool operator==( const MyClass* ob1, const MyStruct& ob2)
    {
         return ob1->getName() == ob2.stringToSearch;
    }
    
    int main()
    {
        vector<MyStruct> vec_struct;
    
        vec_struct.push_back(MyStruct("Hey"));
        vec_struct.push_back(MyStruct("maus"));    
    
        vector<MyClass*> vec_class;
    
        vec_class.push_back(new MyClass("Hey"));
        vec_class.push_back(new MyClass("lolo"));
        vec_class.push_back(new MyClass("maus"));
    
        for(vector<MyStruct>::iterator it = vec_struct.begin(); it != vec_struct.end(); ++it)
        {
              vector<MyClass*>::iterator result = find(vec_class.begin(),vec_class.end(),*it);
    /*
    find versucht ein MyClass* mit einem MyStruct zu vergleichen, deswegen braucht man nen op== dafür
    */
              cout << (*result)->getName() << endl;                                
        }
    
        // vec_class aufräumen
    
        cin.get();
    
        return 0;
    }
    

    Hoffe mal das war das was du wolltst ...



  • @hustbaer
    Deinen Vorschlag habe ich inzwischen umgesetzt. So funktioniert es. Danke noch mal!

    @shinja
    Danke auch dir für die Antwort.
    Bevor ich überhaupt eine Antwort bekommen hatte, habe ich auch einmal mit bind1st/bind2nd herumexperimentiert und versucht aus einem Funktor (bzw. dessen operator()), der zwei Argumente nahm, einen unären zu machen. Hintergrund war ein Beispiel, das ich zu find_if gefunden hatte, bei dem als drittes Argument folgendes übergeben wurde:

    std::bind2nd(std::less<int>(), 1000))
    

    Allerdings bin ich kläglich gescheitert als ich das auf meinen Fall übertragen wollte 😞 .
    Ich glaube zwar, dass das sehr ähnlich zu dem war, was du jetzt vorschlägst, aber vielleicht probiere ich es trotzdem noch einmal.

    @KasF
    Das ist zwar auch ein interessanter Ansatz, allerdings würde ich damit Gleichheit zweier Objekte unterschiedlicher Klassen definieren, sofern eine ihrer Member-Variablen übereinstimmt. Beide Klassen verfügen aber noch über einige andere Member und ich würde somit Objekte vergleichen, die sehr unterschiedlicher Art und eigentlich nicht vergleichbar sind.
    Trotzdem danke für den Hinweis.



  • Würde mich freuen wenn du es nochmal ausprobieren und mir sagen würdest, woran es scheitert. Hilft mir auch.



  • Nanuq schrieb:

    Das ist zwar auch ein interessanter Ansatz, allerdings würde ich damit Gleichheit zweier Objekte unterschiedlicher Klassen definieren

    Stimmt, hast vollkommen Recht. Hab daran gar nicht gedacht ...


Anmelden zum Antworten