it-swarm.com.de

Wie man eine std :: map voller Strings in C++ durchläuft

Ich habe das folgende Problem im Zusammenhang mit dem Durchlaufen eines assoziativen Arrays von Zeichenfolgen, das mit std::map definiert ist.

-- snip --
class something 
{
//...
   private:
      std::map<std::string, std::string> table;
//...
}

Im Konstruktor fülle ich die Tabelle mit Paaren von String-Schlüsseln auf, die den String-Daten zugeordnet sind. An anderer Stelle habe ich eine Methode toString, die ein String-Objekt zurückgibt, das alle Schlüssel und zugehörigen Daten enthält, die im Tabellenobjekt enthalten sind (als Schlüssel = Datenformat).

std::string something::toString() 
{
        std::map<std::string, std::string>::iterator iter;
        std::string* strToReturn = new std::string("");

        for (iter = table.begin(); iter != table.end(); iter++) {
           strToReturn->append(iter->first());
           strToReturn->append('=');
           strToRetunr->append(iter->second());
           //....
        }
       //...
}

Wenn ich versuche zu kompilieren, erhalte ich folgende Fehlermeldung:

error: "error: no match for call to ‘(std::basic_string<char,
    std::char_traits<char>, std::allocator<char> >) ()’".

Könnte mir jemand erklären, was fehlt, was ich falsch mache? Ich fand nur einige Diskussionen über ein ähnliches Problem im Fall von hash_map, wo der Benutzer eine Hashfunktion definieren muss, um hash_map mit std::string-Objekten verwenden zu können. Könnte auch in meinem Fall etwas Ähnliches sein?

62
crazybyte

Ihr Hauptproblem ist, dass Sie im Iterator eine Methode mit dem Namen first() aufrufen. Sie sollen die Eigenschaft first verwenden:

...append(iter->first) rather than ...append(iter->first())

Aus Stilgründen sollten Sie nicht new verwenden, um diese Zeichenfolge zu erstellen.

std::string something::toString() 
{
        std::map<std::string, std::string>::iterator iter;
        std::string strToReturn; //This is no longer on the heap

        for (iter = table.begin(); iter != table.end(); ++iter) {
           strToReturn.append(iter->first); //Not a method call
           strToReturn.append("=");
           strToReturn.append(iter->second);
           //....
           // Make sure you don't modify table here or the iterators will not work as you expect
        }
        //...
        return strToReturn;
}

edit: facildelembrar wies (in den Kommentaren) darauf hin, dass man in modernem C++ die Schleife jetzt neu schreiben kann 

for (auto& item: table) {
    ...
}
73
Tom Leys
  1. Schreiben Sie keine toString()-Methode. Dies ist kein Java. Implementieren Sie den Stream-Operator für Ihre Klasse.

  2. Verwenden Sie die Standardalgorithmen lieber als das Schreiben Ihrer eigenen Schleife. In dieser Situation bietet std::for_each() eine Nice-Schnittstelle für das, was Sie tun möchten.

  3. Wenn Sie eine Schleife verwenden müssen, die Daten jedoch nicht ändern möchten, ziehen Sie const_iterator der iterator vor. Wenn Sie versehentlich versuchen, die Werte zu ändern, werden Sie vom Compiler gewarnt.

Dann:

std::ostream& operator<<(std::ostream& str,something const& data)
{
    data.print(str)
    return str;
}

void something::print(std::ostream& str) const
{
    std::for_each(table.begin(),table.end(),PrintData(str));
}

Wenn Sie es dann drucken möchten, streamen Sie einfach das Objekt:

int main()
{
    something    bob;
    std::cout << bob;
}

Wenn Sie tatsächlich eine Zeichenfolgendarstellung des Objekts benötigen, können Sie lexical_cast verwenden.

int main()
{
    something    bob;

    std::string  rope = boost::lexical_cast<std::string>(bob);
}

Die Details, die ausgefüllt werden müssen.

class somthing
{
    typedef std::map<std::string,std::string>    DataMap;
    struct PrintData
    {
         PrintData(std::ostream& str): m_str(str) {}
         void operator()(DataMap::value_type const& data) const
         {
             m_str << data.first << "=" << data.second << "\n";
         }
         private:  std::ostream& m_str;
    };
    DataMap    table;
    public:
        void something::print(std::ostream& str);
};
19
Martin York

Ändern Sie Ihre Append-Aufrufe, um dies zu sagen

...append(iter->first)

und 

... append(iter->second)

Zusätzlich die Linie

std::string* strToReturn = new std::string("");

ordnet eine Zeichenfolge auf dem Heap zu. Wenn Sie tatsächlich einen Zeiger auf diese dynamisch zugewiesene Zeichenfolge zurückgeben möchten, sollte die Rückgabe in std :: string * geändert werden.

Wenn Sie sich nicht um die Verwaltung dieses Objekts auf dem Heap sorgen möchten, ändern Sie die lokale Deklaration in 

std::string strToReturn("");

und ändern Sie die 'append'-Aufrufe, um die Referenzsyntax zu verwenden ...

strToReturn.append(...)

anstatt 

strToReturn->append(...)

Beachten Sie, dass dies die Zeichenfolge auf dem Stapel erstellt und dann copy in die Rückgabevariable kopiert. Dies hat Auswirkungen auf die Leistung.

4
Adam Holmberg

iter->first und iter->second sind Variablen. Sie versuchen, sie als Methoden aufzurufen.

2
Nick Lewis

Beachten Sie, dass das Ergebnis der Deferenzierung eines std :: map :: iterators ein std :: pair ist. Die Werte von first und second sind keine Funktionen, sondern Variablen.

Veränderung:

iter->first()

zu

iter->first

Gleiches mit iter->second.

2
E.M.

Benutzen:

std::map<std::string, std::string>::const_iterator

stattdessen:

std::map<std::string, std::string>::iterator
1
Dzwiedziu-nkg

Eine weitere sinnvolle Optimierung ist das c_str () -Mitglied der STL string -Klassen, das einen unveränderlichen, nullterminierten String zurückgibt, der alsLPCTSTR, e weitergegeben werden kann. zu einer benutzerdefinierten Funktion, die eine LPCTSTR erwartet. Obwohl ich den Destruktor nicht zur Bestätigung nachverfolgt habe, vermute ich, dass die String-Klasse nach dem Speicher sucht, in dem die Kopie erstellt wird.

0
David A. Gray

in C++ 11 können Sie verwenden

for ( auto iter : table ) {
     key=iter->first();
     value=iter->second();
}
0
james