结构向量的麻烦:解除引用迭代器(可能很容易)

时间:2011-12-03 01:28:39

标签: c++ pointers vector struct

我遇到了这个程序的问题。该程序旨在从一个以逗号分隔的名称形式的文件中获取输入,并将它们计算在输出文件中(我还没有得到)。它在“好到目前为止3”之前失败了。显然,它正在打破的是使迭代器退缩。也就是说,我正在尝试做的是将它用作指针,而++那个指针。这应该跳到数组中的下一个结构。然后,我想使用它 - > inputName来访问struct内部的东西。但是,当我这样做 - > inputName时,它会尝试将它用作指针而不能这样做。不知道从哪里开始,如果我尝试制作像name *那样的常规指针,它不能与nameStorage.begin()一起使用,因为它只需要一个迭代器。我在Windows 7上使用Microsoft Visual Studio 2010,如果有帮助的话。

我对编程很新,所以其他任何提示也都很棒。谢谢!

#include <iostream>
#include <string>
#include <fstream>
#include <vector>

using namespace std;

struct name{
    int tally;
    string inputName;
};

bool die( const string& message );

int main( void ) {
    ifstream infile;
    infile.open( "input.txt", ifstream::in );

    vector<name> nameStorage; // stores all names and tallies of them
    vector<name>::iterator it;
    string tempInput; // Stores the most recent input temporarily
    char temp;
    int counter = 0;
    while ( temp = infile.get(), infile.good() ) { // loop while extraction from file is possible
        if ( temp != ',' ) {
            tolower ( temp ); // makes everything lowercase
            tempInput.push_back(temp); }
        else {
            cout<<"good so far"<<endl;
            for ( it = nameStorage.begin(); it <= nameStorage.end(); it++ ) {
                cout<<"good so far 2"<<endl;
                if ( tempInput == it->inputName ) { 
                    cout<<"good so far 3"<<endl;
                    it->tally++;
                    break;
                }
                else if ( it == nameStorage.end() ) {
                    name tempStruct;
                    tempStruct.inputName = tempInput;
                    tempStruct.tally = 1;
                    nameStorage.push_back( tempStruct );
                }
            }
            tempInput.clear(); // clears the string after it has stored it
        }
        counter++;
        cout<<"Detected "<<counter<<" characters so far."<<endl;
    }
}

bool die( const string& message ) {
    cerr<<message;
    exit (EXIT_FAILURE);
}

5 个答案:

答案 0 :(得分:2)

变化:

for ( it = nameStorage.begin(); it <= nameStorage.end(); it++ )

要:

for ( it = nameStorage.begin(); it != nameStorage.end(); it++ )

移动:

else if ( it == nameStorage.end() ) {
          name tempStruct;
          tempStruct.inputName = tempInput;
          tempStruct.tally = 1;
          nameStorage.push_back( tempStruct );
        }

从你现在更改的for循环中,你不需要else,因为这是for循环结束的条件:

name tempStruct;
tempStruct.inputName = tempInput;
tempStruct.tally = 1;
nameStorage.push_back( tempStruct );

请查看此简单示例here

答案 1 :(得分:2)

此代码至少有4个问题,其中最重要的是使用向量进行此类查找/计数是非常低效的。

1)std :: vector :: end()返回一个特殊类型的迭代器,它不支持布尔运算符,例如&lt; =(但支持operator-或operator- =)。好吧,它支持它们,但行为未定义。

所以

for ( it = nameStorage.begin(); it <= nameStorage.end(); it++ )

应该是

for ( it = nameStorage.begin(); it != nameStorage.end(); it++ )

2)现在你的for语句是正确的,这个比较将永远不会返回true

else if ( it == nameStorage.end() ) {

因此您的新值永远不会存储在向量中。要在nameStorage中查找tempInput,您可以使用std :: find

if (std::find(nameStorage.begin(), nameStorage.end(), tempInput) != nameStorage.end())
{
 /// temp struct blah blah blah
 nameStorage.push_back(tempStruct);
}

3)在字符串上使用布尔等价运算符通常被认为是不良形式,即

if ( tempInput == it->inputName ) {

应该是

if (!tempInput.compare(it->InputName))

但如果您使用std :: find(上面),则不需要这样做。

4)std :: getline支持分隔符,你应该使用它而不是一次读取1个字符,参见http://www.cplusplus.com/reference/string/getline/

最后,您应该使用地图进行查找/计数。 std:地图很好

std::map<std::string, int> nameStorage;

if (nameStorage.find(tempInput) != nameStorage.end())
{
 nameStorage[tempInput]++;
} else
{
 nameStorage[tempInput] =1;
}

答案 2 :(得分:1)

vector的范围[ begin(), end() )end()不包含在该有效元素范围内。 end()指的是一个元素(如果你可以称之为){em>超过vector的结尾。

因此,it == nameStorage.end()显然会出现解除引用迭代器的问题,因为it不会引用现有元素:

if ( tempInput == it->inputName )


该约定将在!= end()上终止,这将消除上述的一个一个问题:

for ( it = X.begin(); it != X.end(); it++ )

迭代器的想法是让内部完全隐藏起来;迭代器的值可能不一定大于下一个元素的值。

在这种情况下,您将减少一次迭代,条件else if (it == nameStorage.end())永远不会成立。但是,您可以在 for循环之后简单地添加该语句的主体,因为一旦循环终止,您就会迭代到vector的末尾。

答案 3 :(得分:0)

您尚未向nameStorage添加任何元素,因此您无法迭代该vector容器的元素。因为你没有在for循环中正确地检查向量迭代器上的边界,所以它允许你进入你的循环,并且在那里你取消引用一个没有指向任何东西的迭代器。通过尝试访问尚未分配的内存,您将调用未定义的行为,如果您损坏内存但未能崩溃,则很可能会导致分段错误或其他一些奇怪的问题。

答案 4 :(得分:0)

添加另一个答案以涵盖有关地图和std :: getline

的问题

映射是一个关联容器,它将键与值相关联。例如,std :: map是一个带有std :: string类型的键的映射,以及一个int类型的值。地图支持快速查找密钥(log(n))。例如

std::map<std::string, int> nameCount;

是否存储与字符串键关联的int值的映射。 (在解释std :: getline之后参见代码示例)。

std :: getline将指定的分隔符视为行尾字符。使用它的常用方法是首先获取由EOL分隔的整行,然后使用分隔符从该行获取行。 ''

这是一些可以达到你想要的代码。

 std::map<std::string, int> nameCount;
 std::ifstream myfile(filename.c_str(),std::ios::in);
 std::string line;
 while(std::getline(myfile, line))
 {
    std::stringstream linestream(line);
    std::string       name;
    while (std::getline(linestream, name, ','))
    {
      if (nameCount.find(name) != nameCount.end())
      {
        nameCount[name]++; // increases the tally for name
      }
      else
      {
        nameCount[name] = 1; // inserts name into map and sets the tally to 1
      }
    }
 }
 // now output the map
 std::map<std::string, int>::iterator namecountitr;
 for (namecountitr = nameCount.begin(); 
       namecountitr != nameCount.end(); namecountitr++)
 {
      std::cout << namecountitr->first.c_str() << ":" 
      << namecountitr->second << std::endl;
 }

享受。