开发者

Copy const char*

开发者 https://www.devze.com 2022-12-21 00:16 出处:网络
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.

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.

0

精彩评论

暂无评论...
验证码 换一张
取 消

关注公众号