Reputation: 1623
I have access to a class (not written by me) that takes a const char*
as a parameter in the constructor. If I have a string that I want to pass the value of as a parameter, what is the safe way to pass it, keeping in mind that the string and the class object may have different scopes?
I don't have access to the source code for the class, so don't assume it's doing something sane like copying the string into a class member.
As a concrete example, this doesn't work:
#include <iostream>
#include <string>
class example {
public:
example(const char*);
const char* str;
};
example::example(const char* a) : str(a) {}
int main() {
std::string* a=new std::string("a");
example thisDoesntWork(a->c_str());
std::cout << thisDoesntWork.str << std::endl;
delete a;
std::cout << thisDoesntWork.str << std::endl; //The pointer is now invalid
a=new std::string("b");
std::cout << thisDoesntWork.str << std::endl;
}
Replacing the constructor with this works (so far as I can tell) but is clearly pretty awful:
example thisDoesWorkButIsAwful((new const std::string(*a))->c_str()); //Memory leak!
Similarly:
char* buffer=new char[a->size()+1];
strcpy(buffer,a->c_str()); //with #include <string.h> added up top
example(buffer);
But again, this is prone to memory leaks.
My main idea at the moment is to make a wrapper class around example
that copies the string into a char *
buffer and deletes the buffer when it goes out of scope, but that seems a little heavy-handed. Is there an easier/better way?
Upvotes: 1
Views: 1837
Reputation: 2979
Fundamentally, something needs to hold on to the memory - either you do it yourself or have it done automatically.
One way to do it automatically:
class SuperThatHoldsIt
{
std::string mString ;
SuperThatHoldsIt ( std::string const& str )
: mString ( str ) { }
} ;
class HoldingExample
: private SuperThatHoldsIt
, public example
{
holdingExample ( std::string const& string )
: SuperThatHoldsIt ( string )
, example ( mString.c_str() )
{ }
} ;
Then create it in a std::shared_ptr
(or boost::shared_ptr
) which will hold on to it.
std::string myString ( "Hello, world!" ) ;
std::shared_ptr<HoldingExample> value = std::make_shared<HoldingExample> ( myString ) ;
Now this holds onto the memory AND the structure.
Notes:
The reason HoldingExample derives from two supers is to that the order of constructors will work out because superclasses are always initialized before local variables. This means we have to construct example
before our own member variables, but we can always initialize a superclass's and use its member variables.
If you pass this into a function, like
callFunction ( *value ) ;
If they hold on to that const char*
after you've let go of your value
, then you'll still have a leak and you really can't get around that.
Upvotes: 1