I have the following class:
class StringHolder
{
public:
StringHolder(std::string& str)
{
m_str = &str;
}
private:
std::string* m_str;
};
And have the following string object (str), with a size of 1,024KB:
char c = 'a';
unsigned long long limit = 1024 * 1024;
std::stringstream stream;
for(int i = 0; i < limit; i++)
{
stream << c;
}
std::string str = stream.str();
Whenever I initialize the StringHolder class with a string, it doesn't make a copy of that string. That's because I used references & pointers, but I'm not sure if I used them properly :\
The question: did I use references & pointers properly?
The correct implementation should be something like
class StringHolder
{
public:
StringHolder(const std::string& str) //note const
: m_str(str){} //use initialization list
private:
std::string m_str; //no need to make it a pointer
};
Under the assumption that the lifetime of the string is known to outlive the object that will refer to it, and that the object will not need to modify the string, then you can take a constant reference to the string in the constructor and store that in:
class StringHolder {
std::string const & str;
public:
StringHolder( std::string const & s ) : str(s) {}
};
With this solution the contents of the string will not be copied, but any change to the original string will affect the contents seen from the reference. Also, this does not allow you to change the string referred to after construction. If you need to reseat the reference you will have to do with a pointer:
class StringHolder {
std::string const * str;
public:
StringHolder( std::string const & s ) : str(&s) {}
void reset( std::string const & s ) {
str = &s;
}
};
The fact that you need to store a pointer instead of a reference is an implementation detail, so you can (as I did) hide it from the user, or you could change the interface to take the string by pointer to make it more explicit (I prefer the former, but the later has the effect of self documentation: it is clearer that lifetimes have to be considered).
Also note that with the implementation as shown, a temporary can be used to initialize/set the string, causing undefined behavior: temporary dies, reference points to nowhere.
Related
Here is the code sample I am working on.
Header file has below code:
class TestClass
{
private:
LPCWSTR m_variable;
public:
TestClass(const std::string& variable);
}
Here is the implementation:
TestClass::TestClass(const std::string& variable)
{
std::wstring stemp = std::wstring(variable.begin(), variable.end());
m_variable= stemp.c_str();
}
Here is the code how I make the call
std::string tempStr = "Panda";
TestClass *test = new TestClass(tempStr);
I stepped through debugger and see that while in constructor the value looks good L"Panda". But as soon as I step out of debugger I no longer see the data for my variable.
stemp.c_str() returns a non-owning pointer to the contents of the string. And std::wstring stemp, which owns the data backing the result of .c_str(), ceases to exist the moment you return from the constructor.
Change your class to store a const std::wstring directly, so you have an owned, persistent copy of the string. You can then safely call .c_str() on the member whenever you need a LPCWSTR:
class TestClass
{
private:
const std::wstring m_variable;
public:
TestClass(const std::string& variable);
}
TestClass::TestClass(const std::string& variable) : m_variable(variable.begin(), variable.end()) {}
I have this code:
class myclass
{
std::string str;
public:
void setStr(std::string value)
{
str=value;
}
std::string getStr()
{
return str;
}
}
main()
{
myclass ms;
std::cout<<ms.getStr()<<std::endl;
}
when I compile and run this code, there is o error and in windows I am always getting str as "".
Is this always valid?
I need the above behavior in the fact that if user did not call set, str would be always a blank string.
should I initialize str in constructor as follow:
class myclass
{
std::string str;
public:
myclass():str(""){}
void setStr(std::string value)
{
str=value;
}
std::string getStr()
{
return str;
}
}
I want to make sure that behavior is the same on all platform and also make sure that code is as small and neat as possible.
Do I need to initialize std::string
No. std::string default constructor initialises a nice empty string for you.
I want to make sure that behavior is the same on all platform and also make sure that code is as small and neat as possible.
Remove clutter then:
struct myclass {
std::string str;
};
Fundamental types though, do not get initialised by default, you need to initialize them explicitly:
struct myclass {
std::string str;
int i = 1; // <--- initialize to 1.
};
You dont need to initialize a string member with an empty string, though it can help to do it anyhows. Consider:
struct foo {
std::string a;
std::string b;
foo() : a("foo") {}
};
Was it by accident or on purpose that b does not get a value in the constructor? I'd prefer
foo() : a("foo"), b() {}
because it makes the intend explicit for no price (not counting a few keystrokes).
I have 10 char* properties of my class called Car,what is the best way to write the setters of these 10 char* properties? One way is to directly set the value in it :
void Car<T>::setKey(const char* toCopyKey)
{
delete[] key;
int length=strlen(toCopyKey);
key=new char[length+1];
strcpy(key,toCopyKey);
}
and do this 10 times ,other solution I thought of , is to make a function that creates a copy of the passed char* and then assigns it in the setter :
char* Car<T>::copyString(const char* s)
{
int length=strlen(s);
char* property=new char[length+1];
strcpy(property,s);
return property;
}
and use the copyString method in every setter like this :
void Car<T>::setModel(const char* toCopyModel)
{
delete[] model;
model=copyString(toCopyModel);
}
But I was wondering if this second solution is correct and if there is a better way to do this copying?I cannot use std::string and vector.
I guess this is an assignment of some C++ course or tutorial, because otherwise I would recommend to question the whole design.
In general, I would learn as early as possible to not do manual memory management at all and use C++ standard library smart pointers. This relieves you from the burden to write destructors, copy|move-assignment and copy|move constructors.
In your example, you could use std::unique_ptr<char[]> to hold the string data. This is also exception safe and prevents memory leaks. Creation of the unique_ptr<char[]> objects can be centralized in a helper method.
class Car {
private:
std::unique_ptr<char[]> model;
std::unique_ptr<char[]> key;
static std::unique_ptr<char[]> copyString(char const* prop) {
auto const len = std::strlen(prop);
auto p = std::make_unique<char[]>(len+1);
std::copy(prop, prop + len, p.get());
p[len] = '\0';
return p;
}
public:
void setModel(char const* newModel) {
model = copyString(newModel);
}
void setKey(char const* k) {
key = copyString(k);
}
char const* getModel() const {
return model.get();
}
};
If you don't know them, I would recommend to read about the rule of zero.
You can combine your two methods by using a reference parameter:
static void Car<T>::setStringProp(char *&prop, const char *toCopyString) {
delete[] prop;
prop = new char[strlen(toCopyString)+1];
strcpy(prop, toCopyString);
}
void Car<T>::setModel(const char *toCopyModel) {
setStringProp(model, toCopyModel);
}
And make sure that your constructor initializes all the properties to NULL before calling the setters, because delete[] prop requires that it be an initialized pointer.
Car<T>::Car<T>(const char *model, const char *key, ...): model(nullptr), key(nullptr), ... {
setModel(model);
setKey(key);
...
}
const fields in C++ must be initialized in the initialization list, this makes non trivial the computation of interdependent values from the constructor parameters.
What is(are) the best way(s) to translate, for example, this piece of java code into c++ ?
public class SomeObject {
private final String some_string;
private final int some_int;
public SomeObject(final String input_filename){
SomeReader reader(input_filename);
some_string = reader.getString();
some_int = reader.getInt();
reader.close();
}
}
I thought of encapsulating a sub-object in SomeObject, but this is just shifting the problem; or constructing the object using a static method:
class SomeObject {
private:
const std::string some_string;
const int some_int;
public:
static SomeObject unserialize(const char * input_filename){
SomeReader reader = new SomeReader(input_filename);
string str = reader.get_string();
int i = reader.get_int();
reader.close();
SomeObject obj(str, i);
return obj;
};
SomeObject(const std::string str, const int i) :
some_string(str),
some_int(i)
{};
}
Is there a better solution ?
Thank you.
This is a great application for C++11 constructor delegation:
class SomeObject {
private:
const std::string some_string;
const int some_int;
public:
// The "real" constructor for SomeObject
SomeObject(std::string str, const int i) :
some_string{std::move(str)},
some_int{i}
{}
// Deserialize from SomeReader (delegates to the primary constructor)
SomeObject(SomeReader& reader) :
SomeObject{reader.get_string(), reader.get_int()} {}
// Deserialize from SomeReader (accepts rvalues,
// delegates to the lvalue constructor)
SomeObject(SomeReader&& reader) :
SomeObject{reader} {}
// Deserialize from named file (delegates to the SomeReader&& constructor)
SomeObject(const char* input_filename) :
SomeObject{SomeReader{input_filename}} {}
};
You can use a delegating ctor and a lambda-function, like this:
SomeObject(const char* filename) : SomeObject([&]{
/* Do calculations here */
return std::make_tuple(...);
}()) {}
SomeObject(std::tuple<...>&& x) : /* ... */ {}
Still, a much better idea is probably a re-design to make use of all the things you can do in C++ and cannot do in Java.
I think you have the right approach.
I would recommend couple of minor changes.
This is not correct C++.
SomeReader reader = new SomeReader(input_filename);
Perhaps you meant:
SomeReader reader(input_filename);
You can change the lines:
SomeObject obj(str, i);
return obj;
to
return SomeObject(str, i);
I have this piece of code that prints the content of a directory using Boost.Filesystem:
class Shell {
private:
const string& command;
const path& firstPath;
const path& secondPath;
public:
// constructor
Shell(const string& _command, const path& _firstPath, const path& _secondPath = path()): command(_command), firstPath(_firstPath), secondPath(_secondPath) {}
//destructor
~Shell() {}
//execute commands
void executeCommand()
{
if(command.compare("ls"))
{
ls();
}
}
void ls()
{
if(exists(firstPath))
{
vector<path> vecPath;
copy(directory_iterator(firstPath), directory_iterator(), back_inserter(vecPath));
sort(vecPath.begin(), vecPath.end());
for(vector<path>::iterator it = vecPath.begin(); it != vecPath.end(); ++it)
{
cout << *it << endl;
}
}
}
};
int main(int argc, char** argv)
{
path Path = "c:\\workspace";
Shell shell("ls", Path); // GOOD
// Shell shell("ls", "c:\\workspace"); BAD creates temporary !!
shell.executeCommand();
}
If I send my second argument direclty as a const char* I know that it will create a temporary and my constructor argument will only be visible in the constructor, then its lost, because of the reference to temporary.
My question is, why isnt the same thing happening to the first argument, the string, because I am sending him also as a const char* directly, but the value isnt lost outside of the constructor ?
Shell shell("ls", Path); // BAD - just happens to work as you expected.
Undefined Behavior - Anything can happen, including acting like you expect. The memory allocated by the temporary just happened to not be overwritten in your case.
std::string *ref;
{
std::string tmp("temp");
ref = &ref; // risky, need to keep lifetime of tmp in mind
const char* const rawptr = ref->c_str(); // equivalent to tmp.c_str()
tmp = "altered"; // *ref also modified, rawptr completely invalid
}
// *ref _and_ rawptr completely invalid.
Is there a specific reason why you store references instead of copies?
By changing your class from
class Shell {
private:
const string& command;
const path& firstPath;
const path& secondPath;
...
to
class Shell {
private:
const string command;
const path firstPath;
const path secondPath;
...
you can avoid lot's of your problems and both cases will be correct.
Why this is the case you can see in the answer of sehe