【问题标题】:How to iterate over a std::map full of strings in C++如何在 C++ 中迭代一个充满字符串的 std::map
【发布时间】:2010-11-07 04:34:21
【问题描述】:

我有以下与迭代使用 std::map 定义的关联字符串数组有关的问题。

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

在构造函数中,我使用与字符串数据关联的字符串键对填充表。在其他地方我有一个方法toString,它返回一个字符串对象,其中包含表对象中包含的所有键和关联数据(作为键=数据格式)。

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());
           //....
        }
       //...
}

当我尝试编译时,出现以下错误:

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

有人可以向我解释缺少什么,我做错了什么吗? 我只在hash_map 的情况下发现了一些关于类似问题的讨论,其中用户必须定义​​一个散列函数才能将hash_mapstd::string 对象一起使用。在我的情况下也可能是类似的东西吗?

【问题讨论】:

  • 欢迎来到堆栈溢出,Crazybyte。很高兴我能提供帮助

标签: c++ dictionary iterator std stdmap


【解决方案1】:

您的主要问题是您在迭代器中调用了一个名为first() 的方法。你要做的是使用名为first的属性:

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

就风格而言,您不应该使用new 来创建该字符串。

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;
}

编辑: facildelembrar 指出(在 cmets 中)在现代 C++ 中您现在可以重写循环

for (auto& item: table) {
    ...
}

【讨论】:

  • 啊,是的!现在我知道(在你解释之后)我正在尝试首先用作方法:(是的,我不应该分配,但这不是原始代码。我正在为字符串分配空间堆并返回对它的引用,而不是将对象复制到堆栈上。感谢您的友好回答!
  • @ crazybyte: 实际上你会返回一个 somthing 的副本,因为返回类型是 std::string。结果是构造回调用者的副本。所以听起来你在某处泄漏内存。
  • 在 c++ 中迭代容器时,这是 i++ 与 ++i 重要的罕见情况。 i++ 创建一个临时容器,而 ++i 没有。我编辑了你的答案,希望你不介意
  • 你需要把'='改成"="
  • 或者更简单的版本for(auto&amp; item : table) {...}
【解决方案2】:
  1. 不要编写toString() 方法。这不是Java。为您的类实现流运算符。

  2. 更喜欢使用标准算法而不是编写自己的循环。在这种情况下,std::for_each() 为您想要做的事情提供了一个很好的界面。

  3. 如果您必须使用循环,但不打算更改数据,则首选const_iterator 而不是iterator。这样,如果您不小心尝试更改值,编译器会警告您。

然后:

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));
}

然后当你想打印它时,只需流式传输对象:

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

如果您确实需要对象的字符串表示,则可以使用lexical_cast

int main()
{
    something    bob;

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

需要填写的细节。

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);
};

【讨论】:

  • 使用流操作符是个好主意。带有签名 std::ostream& operator& pair) 的函数将允许你去 cout
  • @Tom:没有这样的好主意,因为这不是地图迭代器的定义。它可能恰好是您的实现用作映射迭代器的内容(PS。第一个字符串(又名 KEY)必须是常量)。另请注意,我实际上没有在任何地方明确使用迭代器。
  • 那个operator&lt;&lt;()函数不是在返回类型上缺少&amp;吗?
【解决方案3】:

将附加调用更改为 say

...append(iter->first)

... append(iter->second)

另外,一行

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

在堆上分配一个字符串。如果您打算实际返回指向此动态分配的字符串的指针,则应将返回值更改为 std::string*。

或者,如果您不想担心在堆上管理该对象,请将本地声明更改为

std::string strToReturn("");

并更改“附加”调用以使用参考语法...

strToReturn.append(...)

而不是

strToReturn->append(...)

请注意,这将在堆栈上构造字符串,然后将其复制到返回变量中。这会影响性能。

【讨论】:

    【解决方案4】:

    请注意,取消引用 std::map::iterator 的结果是 std::pairfirstsecond 的值不是函数,它们是变量。

    变化:

    iter->first()
    

    iter->first
    

    iter-&gt;second 同上。

    【讨论】:

      【解决方案5】:

      iter-&gt;firstiter-&gt;second 是变量,您试图将它们作为方法调用。

      【讨论】:

        【解决方案6】:

        用途:

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

        改为:

        std::map<std::string, std::string>::iterator
        

        【讨论】:

          【解决方案7】:

          另一个值得优化的是 STL string 类的 c_str ( ) 成员,它返回一个不可变的空终止字符串,可以作为 LPCTSTR 传递,例如。 g.,到需要 LPCTSTR 的自定义函数。虽然我还没有通过析构函数来确认它,但我怀疑字符串类会照顾它创建副本的内存。

          【讨论】:

            【解决方案8】:

            在 c++11 中你可以使用:

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

            【讨论】:

            • for ( auto iter : table ) { KeyType &amp;key=iter.first; ValueType &amp;value=iter.second; }
            猜你喜欢
            • 1970-01-01
            • 2011-10-28
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2017-07-08
            • 2014-05-27
            • 2012-08-28
            • 2019-07-15
            相关资源
            最近更新 更多