Я получаю струну до в качестве параметра от функции, но аргумент, который я получаю, будет уничтоженным позже. Таким образом, я хочу сделать копию из него.
Вот то, что я имею в виду:
class MyClass
{
private:
const char *filename;
public:
void func (const char *_filename);
}
void MyClass::func (const char *_filename)
{
filename = _filename; //This isn't going to work
}
То, чего я хочу достигнуть, не, просто присваивают один адрес памяти другому, но скопировать содержание. Я хочу иметь имя файла как "символ константы*" и не как "символ*".
Я пытался использовать strcpy, но он требует, чтобы целевая строка была неконстантой.
Вокруг существует ли путь? Что-то, не используя const_cast на имени файла?
Спасибо.
Используйте std :: string
, чтобы скопировать значение, поскольку вы уже используете C ++. Если вам нужен const char *
из этого, используйте 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();
}
}
char const *
подразумевает, что класс не владеет связанной с ним памятью. Владельцу всегда нужен неконстантный указатель, потому что в противном случае память не может быть освобождена. Если у вас есть неконстантный указатель, вы можете выделить для него память, а затем использовать strcpy
(или memcpy
) для копирования самой строки. Однако в вашей ситуации использование std :: string
является гораздо лучшим вариантом.
Вы должны решить, хотите ли вы, чтобы имя вашего файла было константным (чтобы его нельзя было изменить) или неконстантным (чтобы его можно было изменить в MyClass :: func).
В стандартной библиотеке C есть функция (если вы хотите пойти по маршруту C) под названием _strdup. Он использует malloc для фактического распределения, поэтому вам нужно будет вызвать free, когда вы закончите со строкой.
Так, например,
void MyClass::func (const char *_filename)
{
if (filename)
{
free(filename);
}
filename = _strdup(_filename);
}
Конечно, не забудьте освободить имя файла в деструкторе.
[Предполагая, что вы продолжаете реализовывать внутренние компоненты своего класса в стиле C, что может быть или не быть полезным с точки зрения скорости разработки и выполнения (в зависимости от дизайна всего проекта), но обычно не рекомендуется в пользу std :: string
и друзья.]
Превращение
const char *filename;
в
char *filename;
не сделает вас счастливыми с strcpy
, поскольку вам действительно нужна память для копии вашей строки :)
Относительно части кода ручного управления памятью см. Ответ Тадеуша Копека, который, кажется, все в порядке.
Также имейте в виду, что существует разница между
const char *filename; // "filename" points to "const char"
// and is not const itself
char const *filename; // semantically the same as above
и
char * const filename; // "filename" is const and points to "char",
// which is not const
. В первом случае вы можете указать filename
на любую другую строку const char
, во втором вы можете изменить эту строку только «на месте» (так, чтобы значение filename
оставалось неизменным, поскольку оно указывает на то же место в памяти). Конечно, при необходимости можно комбинировать эти два (или ни один из них).
P.S. Если вы назвали параметр своей функции-члена _filename
только для того, чтобы избежать конфликта имен с переменной-членом filename
, вы можете просто префикс this
(и избавиться от подчеркивание):
void MyClass::func (const char *filename)
{
...
this.filename = copy;
}
Почему у вас это как const, если вам нужно изменить их в одном из методов класса.
В любом случае нестатическим константным элементам данных и ссылочным элементам данных нельзя присвоить значения; вы должны использовать список инициализации с конструктором для их инициализации.
MyClass::MyClass(const char *_filename) : filename( _filename )
{
// filename = _filename; This isn't going to work
}
Инициализатор также может вызывать функцию, как показано ниже.
MyClass::MyClass(const char *_filename) : filename( getfilename() )
{
// filename = _filename; This isn't going to work
}
Не проверял этот конкретный случай, который является подходящим, но список инициализации - это способ присвоения значений нестатическим константным элементам данных.
Если вы хотите придерживаться обычного C, используйте strncpy. Но я согласен с Ильей, используйте std::string, так как это уже C++. Если это ваше приложение вызывает ваш метод, вы можете даже получить std::string в первую очередь, так как исходный аргумент будет уничтожен.
Я согласен, что лучше всего (по крайней мере, не зная ничего больше о вашей проблеме) использовать std::string
. Но если вы настаиваете на самостоятельном управлении памятью, то вы должны управлять ею полностью. Итак, путь C++:
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;
}
и путь C
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?
}