I'm receiving a c-string as a parameter from a function, but the argument I receive is going to be destroyed later. So I want to make a copy of it.
Here's what I mean:
class MyClass
{
private:
const char *filename;
public:
void func (const char *_filename);
}
void MyClass::fu开发者_开发知识库nc (const char *_filename)
{
filename = _filename; //This isn't going to work
}
What I want to achieve is not simply assign one memory address to another but to copy contents. I want to have filename as "const char*" and not as "char*".
I tried to use strcpy but it requires the destination string to be non-const.
Is there a way around? Something without using const_cast on filename?
Thanks.
Use a std::string
to copy the value, since you are already using C++. If you need a const char*
from that, use c_str()
.
class MyClass
{
private:
std::string filename;
public:
void setFilename(const char *source)
{
filename = std::string(source);
}
const char *getRawFileName() const
{
return filename.c_str();
}
}
I agree that the best thing (at least without knowing anything more about your problem) is to use std::string
. But if you insist on managing memory by yourself, you have to manage it completely. So the C++ way:
class MyClass
{
private:
const char *filename;
MyClass(const MyClass&); // no implementation
MyClass operator=(const MyClass &); // no implementation
public:
MyClass() {filename = 0;}
~MyClass() {delete[] filename;}
void func (const char *_filename);
}
void MyClass::func (const char *_filename)
{
const size_t len = strlen(_filename);
char * tmp_filename = new char[len + 1];
strncpy(tmp_filename, _filename, len);
tmp_filename[len] = '\0'; // I'm paranoid, maybe someone has changed something in _filename :-)
delete[] filename;
filename = tmp_filename;
}
and the C way
class MyClass
{
private:
const char *filename;
MyClass(const MyClass&); // no implementation
MyClass operator=(const MyClass &); // no implementation
public:
MyClass() {filename = 0;}
~MyClass() {free(filename);}
void func (const char *_filename);
}
void MyClass::func (const char *_filename)
{
free(filename);
filename = strdup(_filename); // easier than C++, isn't it?
}
There's a function in the Standard C library (if you want to go the C route) called _strdup. It uses malloc to do the actual allocation so you will need to call free when you're done with the string.
So for example,
void MyClass::func (const char *_filename)
{
if (filename)
{
free(filename);
}
filename = _strdup(_filename);
}
Of course, don't forget to free the filename in your destructor.
You have to decide whether you want your file name to be const (so it cannot be changed) or non-const (so it can be changed in MyClass::func).
[Assuming you continue implementing your class' internals in the C-style, which may or may not be beneficial in terms of development and execution speed (depending on the whole project's design) but is generally not recommended in favor of std::string
and friends.]
Turning
const char *filename;
into
char *filename;
will not make you happy with the strcpy
, since you actually need some memory for a copy of your string :)
For the manual memory management code part, please see Tadeusz Kopec's answer, which seems to have it all right.
Also, keep in mind that there is a difference between
const char *filename; // "filename" points to "const char"
// and is not const itself
char const *filename; // semantically the same as above
and
char * const filename; // "filename" is const and points to "char",
// which is not const
In the first case, you can make filename
point to any other const char
string, in the second, you can only change that string "in-place" (so keeping the filename
value the same, as it points to the same memory location). Of course one can combine these two (or none of them) if needed.
P.S. If you name your member function's parameter _filename
only to avoid naming collision with the member variable filename
, you can just prefix it with this
(and get rid of the underscore):
void MyClass::func (const char *filename)
{
...
this.filename = copy;
}
If you want to stick to plain C, use strncpy. But I agree with Ilya, use std::string as it's already C++. If it's your application that's calling your method, you could even receive a std::string in the first place as the original argument is going to be destroyed.
Why do you have it as const, If you need to change them in one of the methods of the class.
Anyways, non-static const data members and reference data members cannot be assigned values; you should use initialization list with the constructor to initialize them.
MyClass::MyClass(const char *_filename) : filename( _filename )
{
// filename = _filename; This isn't going to work
}
An initializer can also call a function as below
MyClass::MyClass(const char *_filename) : filename( getfilename() )
{
// filename = _filename; This isn't going to work
}
Didn't verify this particular case which is the apt one, but initialization list is the way to assign values to non static const data members.
char const*
implies that the class does not own the memory associated with it. The owner always needs a non-const pointer because otherwise the memory couldn't be freed. When you have non-const pointer, you can allocate the memory for it and then use strcpy
(or memcpy
) to copy the string itself. However, in your situation using std::string
instead is a much better option.
精彩评论