复制常量字符指针

16

我从一个函数中接收到一个C字符串作为参数,但是我接收到的参数将会在后面被销毁。因此,我想要复制它。

这就是我的意思:

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
}

我想要实现的不仅仅是将一个内存地址指定给另一个,而是要复制内容。我想将文件名作为“const char*”,而不是“char*”。
我尝试使用strcpy,但它需要目标字符串是非常量的。
有没有一种方法可以避免这个问题?而且不需要在文件名上使用const_cast?
谢谢。
8个回答

29

使用 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();
    }
}

7
更好的做法是使用隐式转换:filename = source; - Tronic
11
隐式转换从来不会是“更好的”。 - Peter Alexander
4
实际上这并不是转换,因为字符串对于char const*已经重载了op=运算符,但是它仍然大约快了13倍。 - Roger Pate
嗯... filename.assign(source) - D.Shawley

9

我认为(至少在不了解你的问题更多细节之前)最好的方法是使用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?
}

1
你的类还需要一个拷贝构造函数和赋值运算符。 - anon
1
谢谢。通过使MyClass不可复制,已经解决了这个问题 :-) - Tadeusz Kopec for Ukraine
//我很多疑,也许有人在_filename中做了什么改变 :-)邪恶的线程?:)) - mlvljr

4

标准C库中有一个函数(如果您想使用C语言),叫做_strdup。它使用malloc来进行实际的内存分配,所以当您完成字符串操作后需要调用free函数。

例如:

void MyClass::func (const char *_filename)
{
    if (filename)
    {
        free(filename);
    }
    filename = _strdup(_filename);
}

当然,在你的析构函数中别忘了释放文件名。

@Neil Butterworth 你又来了!:)) - mlvljr
"strdup" 是 POSIX 标准,但已被弃用。然而 "_strdup" 符合 ISO C++ 标准。 - Cthutu

2
假设您继续以C风格实现类的内部,这可能会在开发和执行速度方面有利或不利(取决于整个项目的设计),但通常不建议使用它,而应使用std::string等工具。

转换:

const char *filename;

转换为

char *filename;

使用strcpy并不能让您满意,因为您实际上需要一些内存来复制字符串 :)

对于手动内存管理代码部分,请参见Tadeusz Kopec的答案,他似乎把所有事情都做得很对。

此外,请记住以下区别:

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值相同,因为它指向相同的内存位置)。当然,如果需要,可以将这两个(或其中任何一个)结合起来。
附言:如果您仅为了避免与成员变量filename命名冲突而命名成员函数的参数为_filename,则可以使用this前缀(并且去掉下划线):
void MyClass::func (const char *filename)
{
 ...
 this.filename = copy;
}

1
没有先分配内存吗?哎呀! - Pontus Gagge
1
好的,这是可行的。通过展示C语言的做法来为C++辩护总是很好的!;-) - Pontus Gagge
啊... 析构函数中没有提到释放内存? - D.Shawley
@ D.Shawley 这个回答显然是因为踩而被推动的,修复一下吧:)) - mlvljr
哎呀!噫!..复制构造和赋值在哪里提到了?我要给自己点个踩。..失败了 :( - mlvljr
1
@mlvljr 呵呵呵...我忘记了那些 ;) - D.Shawley

2

你需要决定你的文件名是const(因此不能更改)还是non-const(因此可以在MyClass :: func中更改)。


0

如果你需要在类的某个方法中更改它们,为什么要将其设置为const呢?

无论如何,非静态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 
}

没有验证这个特定的情况,但初始化列表是为非静态const数据成员分配值的方法。


0
如果你想坚持使用纯C,可以使用strncpy。但我同意Ilya的观点,使用std::string,因为它已经是C++的一部分了。如果是你的应用程序调用你的方法,你甚至可以在第一次接收到std::string作为原始参数,因为原始参数将被销毁。

-1

char const* 意味着该类不拥有与其关联的内存。所有者始终需要一个非常量指针,否则无法释放内存。当您拥有非常量指针时,可以为其分配内存,然后使用 strcpy(或 memcpy)来复制字符串本身。但是,在您的情况下,使用 std::string 更好。


3
不,这里指针并不是const,而是指向的内存是const。拥有一个 char const 的指针是完全可以的,你可以绝对地释放它所指向的内存。 - Konrad Rudolph
2
即使指针是常量(如char* const),您仍然可以使用delete[]或free()删除它。 - Roger Pate
1
@Tronic:即使它是“指向常量”的(例如char const*),你仍然可以使用delete[]或free()释放它。(free()需要一个const_cast,但这纯粹是由于C的遗留问题,正如delete/delete[]没有那个小问题所示。)我认为真正的问题是你试图通过const来暗示所有权,这是你无法做到的。比较string::c_str和取vector::front的地址(例如&v.front()&v[0]);有不同的const,但这两个内存片段分别由string和vector拥有。 - Roger Pate
2
@Tronic:什么?free()函数可以追溯到C语言还没有const关键字的时代,这就是我所说的遗留问题。我认为你对const正确性感到困惑了。销毁const对象是可以的,也是必须的。请注意这里两个int都被销毁了:int main() { int const* p = new int const(42); delete p; int const other = 42; } - Roger Pate
@Tronic:我也曾经有过这样的经历,它可能会让人沮丧,但在大局上只是几个声望点,不必担心。你用问题代替继续评论是正确的,但我希望你能够少一些争论(没有必要提到Linus;请求权威参考,然后描述“许多”“程序员”和“大多数库”等)。 - Roger Pate
显示剩余7条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接