I am new to C++ programming (work with Java mostly), and this behavior of C++ classes, member strings and string conversions to const char*
with c_str()
is confusing me.
I have a header, a class and main function as follows:
sample.h
class Sample
{
private:
int id;
std::string text;
public:
Sample(int id);
void setId(int id);
int getId();
void setText(std::string txt);
std::string getText();
void loadText();
~Sample();
}
sample.cpp
Sample::Sample(int id)
{
this->id = id;
}
void Sample::setId(int id)
{
this->id = id;
}
int Sample::getId()
{
return this->id;
}
void Sample::setText(std::string txt)
{
this->text = txt;
}
std::string Sample::getText()
{
return this->text;
}
void Sample::loadText()
{
this->text = "Loaded";
}
Sample::~Sample()
{
std::cout << "Destructor is called." << std::endl;
}
main.cpp
void main()
{
int id = 1;
Sample* sample = new Sample(id);
// Case: 1 - If I do this, it does not work. Prints gibberish.
sample->loadText();
const char* text = sample->getText().c_str();
std::cout << text << std::endl;
// Case: 2 - Otherwise, this works.
sample->loadText();
std::cout << sample->getText().c_str() << std::endl;
// Case: 3 - Or, this works
sample->loadText();
std::string txtCpy = sample->getText();
const char* text = textCpy.c_str();
std::cout << text << std::endl;
}
All three cases are done one at a time.
Case 3 does satisfy my use case (which is, passing the string to a C library that expects a const char*
. But, I can't figure out the difference between Case: 1 and Case: 3? If we are returning the string by value, how does copying it to an intermediate variable make it kosher for the run-time?
The result of c_str()
is only valid while the string you called it on still exists. In case 3, txtCpy
still exists at the point you are writing cout << text
. But in Case 1, the string was the return value of sample->getText
which is temporary and stop existing at the end of that line .
This issue always will exist if you take pointers or references to other objects. A naked pointer or reference has its own lifetime which may differ from the lifetime of the targeted object. This is unlike Java where object references all participate in the lifetime of the object.
As such, you always need to think about object lifetimes when using these features, and it's commonly recommended to instead use higher level features or other code styles that do not permit lifetime management errors.
You could consider adding a member function to Sample which gets a const char *
pointing at the original string (although this is a wee violation of encapsulation, and still has a similar class of problem if you hold onto the pointer and then modify the underlying string). Better would be to just avoid working with the naked pointers entirely.