How to have a file stream as a class member

2019-03-06 02:35发布

问题:

I have the following parser class that works in Visual C++

class Parser
{
   private:
   const char* filename;
   std::ifstream filestream;
   std::vector<std::string> tokens;
   unsigned int linect;

   public:
   Parser(const char* filename);
   bool readline();
   std::string getstrtoken(unsigned int i) const { return tokens[i]; }
   int getinttoken(unsigned int i) const { return atoi(tokens[i].c_str()); }
};

Parser::Parser(const char* filename) :
   filename(filename),
   linect(0)
{
   filestream = ifstream(filename); // OK in VC++, not with GCC?
}

bool Parser::readline()
{
   std::string line;
   getline(filestream, line);
   std::stringstream ss(line);
   std::string token;

   tokens.clear();
   while(getline(ss, token, ' ')){ if(token != "") tokens.push_back(token); }
   linect++;
   return (filestream != NULL);
}

But when I try to compile it with GCC 4.8.2, I get errors saying that I cannot assign to filestream. From what I read elsewhere on this site, you can do

std::ifstream filestream(filename);

but you cannot do

std::ifstream filestream;
filestream = ifstream(filename);

which is essentially what I need to do if I want to declare filestream as a member of the Parser class and initialize it in the constructor.

I would like to have the file stream kept within the Parser class so that those who use the parser don't need to declare and keep track of it. It seems to me that this should be self-contained in the Parser class since its internal methods (e.g. readline()) are the only ones that use it.

Is there a way to achieve this that works with both platforms?

Thanks.

edit: My fix was to explicitly call the open() method of ifstream. My parser class constructor now looks like:

Parser::Parser(const char* filename) :
   filename(filename),
   linect(0)
{
   filestream.open(filename);
   // Do some checking to make sure the file exists, etc.
}

回答1:

You can't, since std::ifstream has deleted copy constructor and copy assignment. You may get around by doing

filestream.swap(ifstream(filename)).

The fact that it compiles on visual studio is mostly because it gets inlined into either move assignment or move constructor(I'm not so good to tell you which exactly). If you try

std::ifstream myF;
filestream = myF;

it won't compile.

However you may try to do the move I wrote, or you can just call .open(http://en.cppreference.com/w/cpp/io/basic_ifstream/open)



回答2:

std::ifstream don't have a copy constructor, probably one of the many extensions of VC++. Correct code is:

Parser::Parser(const char* filename) :
   filename(filename),
   linect(0),
   filestream(filename)
{
}

Please take note of member variable and parameter filename. Use this-> or change name (recommended, normally prefix is used for member variables _ or m_)



回答3:

I think a better solution would be for you to:

  1. Construct a ifstream first.
  2. Construct a Parser using the ifstream object.
  3. Change Parser to store a reference to an istream object. This allows you the flexibility of being able parse the contents of a file, stdin, and a string.

class Parser
{
   private:
      std::istream& str;
      std::vector<std::string> tokens;
      unsigned int linect;

   public:
      Parser(std::istream& s) : str(s) ... {}

      ...
};