C++ string member construction - c++

How do I go about using the constructor for a member which is a string? Here is an example (which is wrong I realize)
class Filestring {
public:
string sFile;
Filestring(const string &path)
{
ifstream filestream(path.c_str());
// How can I use the constructor for the member sFile??
// I know this is wrong, but this illustrates what I want to do.
string sFile((istreambuf_iterator<char>(filestream)), istreambuf_iterator<char>());
}
};
So basically I want to be able to use the member sFile's constructor without doing a string copy. Is there a way to achieve this through assignment?

The best you can use is string::assign:
sFile.assign(istreambuf_iterator<char>(filestream), istreambuf_iterator<char>());
But in C++11 there is a move assignment operator for string so doing the following is almost as efficient (there is no copy of character data, the character data are moved):
sFile = string(istreambuf_iterator<char>(filestream), istreambuf_iterator<char>());
Yet another trick when C++11 move assignment is not avaliable is to use std::swap or string::swap. The efficiency would be probably almost identical to the move assignment variant.
string newContent(istreambuf_iterator<char>(filestream), istreambuf_iterator<char>());
std::swap(sFile, newContent); // or sFile.swap(newContent);

One way is to make filestream a member, so it can be referred to in the constructor initialization list:
class Filestring {
private:
ifstream filestream;
public:
string sFile;
Filestring(const string &path) : filestream(path.c_str()), sFile((istreambuf_iterator<char>(filestream)), istreambuf_iterator<char>())
{}
};
For this to work properly, filestream must appear in the class definition before sFile, or else it won't be initialized in time to use it in the sFile constructor. Of course, you're also adding to the overhead of the Filestring class.
A safer technique which also avoids the overhead of string copying is to use string::swap():
class Filestring {
public:
string sFile;
Filestring(const std::string &path)
{
std::ifstream filestream(path.c_str());
sFile.swap(string((istreambuf_iterator<char>(filestream)), istreambuf_iterator<char>()));
}
};

Not really, you're bound to do a copy if you don't initialize members inside the initializer list, which in your case doesn't seem possible.
Note that your code doesn't initialize the member, but creates a new local variable.
The proper way would be
sFile = std::string((istreambuf_iterator<char>(filestream)), istreambuf_iterator<char>());

You can do it.
But its not worth the extra complexity. If you want to avoid the cost of a copy (which a decent compiler will probably do anyway). You can load it into a temporary then use std::swap().
I would do:
class Filestring {
public:
string sFile;
Filestring(const string &path)
{
ifstream filestream(path.c_str());
string data((istreambuf_iterator<char>(filestream)), istreambuf_iterator<char>());
// Just swap the internal data structures:
std::swap(sFile, data);
}
};

Use initialization lists:
Filestring(const string &path):sFile(/*what you're initializing with*/)
{//more
}

sFile.assign(constructor args);

Related

ifstream as class member [duplicate]

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.
}
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)
I think a better solution would be for you to:
Construct a ifstream first.
Construct a Parser using the ifstream object.
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) ... {}
...
};
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_)

Error when creating a string in a function of class

I create a class named Employee, in private, I have a Name as a string . here is my class declaring:
class Employee
{
string Name;
public:
Employee();
void SetName(string);
void StringToEmployee(string);
~Employee();
}
this is definition of StringToEmployee(string) method:
void Employee::StringToEmployee(string s)
{
char *first = s, *end = s+strlen(s), *last = NULL;
last = find(first, end, ',');
string temp(first, last- first);
SetName(temp);
}
The error occurs when I debug to the line string temp(first, last- first), it's seem to the compiler does not allow me to construct a new string in method. cause I have also changed into string temp; then temp.assign(first, last-first). the error still remain. How could I create a new string in a method?
You should be using iterators and taking advantage of the features of the standard library, rather than raw pointers and C-style string functions. Not only will this give you more idiomatic and easier to understand C++ code, but it will also implicitly resolve many of your errors.
First, the implementation of StringToEmployee should be rewritten as follows:
void Employee::StringToEmployee(std::string s)
{
const std::string temp(s.begin(),
std::find(s.begin(), s.end(), ',');
SetName(temp);
}
But since you are not modifying the s parameter and do not need a copy of it, you should pass it by constant reference:
void Employee::StringToEmployee(const std::string& s)
{
const std::string temp(s.begin(),
std::find(s.begin(), s.end(), ',');
SetName(temp);
}
Also, you should consider redesigning your Employee class. Currently, you have a default constructor that creates an invalid Employee object, and then you have member functions that allow you to turn that invalid Employee object into a valid one by settings its members. Instead, you could have a constructor that did all of this initialization for you, in one step. Not only would your code be cleaner and easier to understand, but it would be more efficient, too!
Perhaps something like:
class Employee
{
std::string Name; // name of this employee
public:
Employee(const std::string& name); // create Employee with specified name
void SetName(const std::string& newName); // change this employee's name
~Employee();
};
Employee::Employee(const std::string& name)
: Name(s.begin(), std::find(s.begin(), s.end(), ','))
{ }
void Employee::SetName(const std::string& newName)
{
Name = std::string(s.begin(), std::find(s.begin(), s.end(), ','));
}
Employee::~Employee()
{ }
A couple of quick notes:
You'll see that I always explicitly write out std:: whenever I use a class from the standard library's namespace. This is a really good habit to get into, and it's not really that hard to type an extra 5 characters. It's particularly important because using namespace std; is a really bad habit to get into.
I pass objects (like strings) that I don't need to modify or have a copy of inside of the method by constant reference. This is both easier to reason about, and also potentially more efficient (because it avoids unnecessary copies).
Inside of the constructor, I have used what may appear to be a funny-looking syntax, involving a colon and some parentheses. This is called a member initialization list, and it's something you should get used to seeing. It's the standard way for a class's constructor to initialize its member variables.
For some reason you want to assing std::string to char*.
Judging from other your code, you want to work with raw char array, so, you need to put correct pointers to first and last like this:
char *first = &s[0], *end = (&s[0]) + strlen(s.c_str()), *last = NULL;
And this part:
string temp(first, last- first);
is incorrect, because last - first is pointer, and, as I understand, you want to use std::string(const char*, size_t) constructor. But instead, you are using iterator-based constructor and system is correctly dying, because first pointer is larger, than second one.
As you see, your method is error-prone. I recommend re-do this part of code, using iterators, like this:
void Employee::StringToEmployee(string s)
{
auto found = find(s.begin(), s.end(), ',');
string temp(s.begin(), found);
SetName(temp);
}

How to have a file stream as a class member

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.
}
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)
I think a better solution would be for you to:
Construct a ifstream first.
Construct a Parser using the ifstream object.
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) ... {}
...
};
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_)

How to initialize a constructor with that takes Strings as parameters?

I am not sure that I am using the right terminology, but question is how do I properly make a constructor that takes a string in as a parameter?
I am used to having a const char * in the constructor instead of strings.
Normally I would do something like this:
Name(const char* fName, const char* lName)
: firstName(0), lastName(0)
{
char * temp = new char [strlen(fName) + 1];
strcpy_s(temp, strlen(fName) + 1, fName);
firstName = temp;
char * temp2 = new char [strlen(lName) + 1];
strcpy_s(temp2, strlen(lName) + 1, lName);
lastName = temp2;
}
What if the constructor is this:
Name(const string fName, const string lName) { }
Do I still do base member initialization? do I still need to use string copy in the base of the constructor?
Use std::string and initializer lists:
std::string fName, lName;
Name(string fName, string lName):fName(std::move(fName)), lName(std::move(lName))
{
}
In this case, you don't need to use terribly bare pointers, you don't need allocate memory, copy characters and finally de-allocate. In addition, this new code has chances to take advantages of moving rather than copying since std::string is movable. Also it's useful to read this.
And so on....
I see that you have already accepted an answer but I would like to expand upon the answers.
As deepmax said, if you pass by value you can write your constructor to take advantage of "move semantics". This means instead of copying data, it can be moved from one variable to another.
Written like so:
class Name{
public:
Name(std::string var): mem_var(std::move(var)){}
std::string mem_var;
};
Which seems like a good idea, but in reality is no more efficient than the copy constructor
class Name{
public:
Name(const std::string &var): mem_var(var){}
std::string mem_var;
};
The reason this is, is because in the general use case that looks like this:
auto main() -> int{
Name name("Sample Text");
}
only one copy will ever get made either way (see copy elision), and in the other case of
auto main() -> int{
std::string myname = "Hugh Jaynus";
Name name(myname);
}
2 copies will be made in the 'efficient' pass-by-value move semantics way!
This is a good example of when the copy constructor (or pass-by-reference) should be used, not an example against it.
On the contrary...
If you write an explicit constructor that makes use of move semantics you could get an efficient solution no matter the circumstance.
Here is how you might write out a name class definition with both constructors:
class Name{
public:
Name(const std::string &first_, const std::string &last_)
: first(first_), last(last_){}
Name(std::string &&first_, std::string &&last_) // rvalue reference
: first(std::move(first_)), last(std::move(last_)){}
std::string first, last;
};
Then when you use the class the more efficient path should be taken.
If we go back to our examples we can rewrite them to make use of the best or most efficient constructor:
int main(){
// pass by reference best here
Name myname("Yolo", "Swaggins");
// move most efficient here
// but never use 'first' and 'last' again or UB!
std::string first = "Hugh", last = "Jaynus";
Name yourname(std::move(first), std::move(last));
}
Never just take for granted that one solution is better than all others!
I'm used to do this:
std::string fName;
std::string lName;
Name(const std::string &fName, const std::string &lName) :
fName(fName), lName(lName)
{
}
Using the references saves the work of copying the strings to a new object on the stack, it will just pass the reference to the existing string. Once you are assigning them to the class members, they will get copied.
if you want to keep const char * as your constructor input types do this.
std::string fName;
std::string lName;
Name(const char *_fName, const char *_lName) :
fName(_fName), lName(_lName)
{
}
You can construct a std::string from a const char.

Initializing private istream data member in C++

I'm currently trying to initialise a private istream variable in class.
The class definition looks like:
#define PARSER_H
class parser {
public:
parser();
parser(string predict_table_file_name);
private:
int getMaxRHS(string predict_table_file_name);
int getMaxPairs(string predict_table_file_name);
int getMaxPairsY(string predict_table_file_name);
int getMaxRHSY(string predict_table_file_name);
int getMaxSymbols(string predict_table_file_name);
int getGoalSymbol(string predict_table_file_name);
int getNumberOfTerminalSymbols(string predict_table_file_name);
string getSymbol(int symbolID);
string getToken();
string openFile(string sourceFile);
bool isTerminalSymbol(string token, string symbolArray[], int terminalSymbols);
istream scanFile;
};
#endif
The variable in question is "istream scanFile". The code I'm using to try and initialize it looks like this.
string parser::openFile(string sourceFile) {
filebuf fb;
fb.open(sourceFile.c_str(), ios::in);
parser::scanFile(&fb);
}
The line "parser::scanFile(&fb);" is giving me the trouble. Apparently the compiler thinks I'm trying to call function, which I guess I am, but I just want to call the constructor on parser::scanFile.
I'm new-ish to C++, so any help would be greatly appreciated.
To solve your question you can add the filebuf as a member variable.
class parser
{
// STUFF LIKE BEFORE
filebuf fb;
istream scanFile;
};
parser::parser()
:fb()
,scanFile(&fb)
{}
string parser::openFile(string sourceFile)
{
fb.open(sourceFile.c_str(), ios::in);
}
But you should probably be using an fstream object:
class parser
{
// STUFF LIKE BEFORE
ifstream scanFile;
};
parser::parser()
:scanFile()
{}
string parser::openFile(string sourceFile)
{
scanFile.open(sourceFile.c_str());
}
see: http://www.cplusplus.com/reference/iostream/fstream/
You must do such initialization in the initialization list of the constructor. Why do you want to implement openFile as a separate function?
Also, if scanFile is used for reading from files, why not use ifstream instead?
Are you going to create parsers that are not associated with files?
If not, try this for the constructor:
parser(string sourceFile) : scanFile(sourceFile.c_str()) { ... other constructor stuff ... }
That assumes that scanFile is a ifstream. Using a istream is more complex. Your original code would not have worked, because the filebuf is destroyed when you exit the openFile function.
You can't, because the object is already constructed. But you can
scanFile.rdbuf( &fb );
but that won't do either, because fb will be destructed as soon as you return from the openFile. But you'll have to resolve this problem yourself, because it's your code flow.
You want something like:
parser::scanFile = istream(&fb);
to invoke the constructor.