std::string 作为成员函数的参数的奇怪行为

Strange behavior of std::string as an argument of a member function

本文关键字:参数 成员 string std 函数      更新时间:2023-10-16

我有一个类:

class MyClass
{
    char *filename1;
    char *filename2;
public:
    void setFilename1(std::string str)
    {
        filename1 = const_cast<char*>(str.c_str())
    }
    void setFilename2(std::string str))
    {
        filename2 = const_cast<char*>(str.c_str())
    }
    void function()
    {
      // do semthing
    }
    void printFilename1()
    {
      std::cout<<filename1<<std::endl;
    }
}

这是我的主要功能:

MyClass *p = new MyClass();
p->setFilename1("first_string");
p->printFilename1();
p->setFilename2("second_string");
p->printFilename1();

输出对我来说非常令人惊讶:

first_string

second_string

我发誓我的函数MyClass::setFilename2没有错别字,我没有两次设置文件名2变量。

我使用的是 g++ 编译器版本 4.8.4。以下是我编译类的方法:

g++ -g -O -Wall -fPIC -pthread -std=c++11 -Wno-deprecated-declarations -m64 -I/home/user/root-6.06.00/include -c myClass.cxx

现在,另一个惊喜:当我更改MyClass::setFilename函数时:

void setFilename2(char* str))
    {
        filename2 = str;
    }

我得到了我期望的输出:

first_string

first_string

执行函数 MyClass::function() 不会更改任何字符串的值。

这到底是怎么回事呢?这与我对C++的了解相矛盾。如果一个函数不引用相同的变量并且彼此无关,它们如何对另一个函数产生影响?

我想这可能与编译器版本或某些编译器选项有关。但我不知道发生了什么。

编辑:你能向我解释为什么这段代码的行为方式吗?

c_str()返回指向char数组的指针,只要std::string不被修改,该数组就保持有效;在您的情况下,您调用c_str()std::string对象在方法返回后立即被销毁(它是从字符串文本动态创建的临时对象),因此您有效地存储了指向已解除分配的内存的指针。当您执行printFileName1时看到新值的事实只是分配器正在回收之前用于其他字符串的内存位置这一事实的副作用;就标准而言,这一切都是未定义的行为(您可以预期悲剧性的崩溃)。

正确的方法是将std::string直接存储在类中,它将在MyClass实例的整个生命周期内正确管理自己的内存。

class MyClass
{
    std::string filename1;
    std::string filename2;
public:
    void setFilename1(std::string str)
    {
        filename1 = str;
    }
    void setFilename2(std::string str))
    {
        filename2 = str;
    }
    void function()
    {
      // do semthing
    }
    void printFilename1()
    {
      std::cout<<filename1<<std::endl;
    }
}
MyClass *p;
p->setFilename1("first_string");
p->printFilename1();
p->setFilename2("second_string");
p->printFilename1();

你永远不会为 p 指向的内容分配内存,因此您在 *p 上调用的任何内容都将是未定义的行为

例如,

我认为这也是错误的。

void setFilename1(char* str)
{
    std::string buf = str;
    filename1 = const_cast<char*>(buf.c_str())
}

因为"buf"被删除在范围之外。

这没关系。

void setFilename1(std::string str)
{
    static std::string buf = str;
    filename1 = const_cast<char*>(buf.c_str())
}

其他方法:

class MyClass
{
    char *filename1;
    char *filename2;
public:
    void setFilename1(std::string& str)
    {
        filename1 = const_cast<char*>(str.c_str())
    }
    void setFilename2(std::string& str))
    {
        filename2 = const_cast<char*>(str.c_str())
    }
    void function()
    {
      // do semthing
    }
    void printFilename1()
    {
      std::cout<<filename1<<std::endl;
    }
}
MyClass *p = new MyClass();
string str1 = "first_string";
p->setFilename1(str1);
p->printFilename1();
string str2 = "second_string";
p->setFilename2(str2);
p->printFilename1();
delete p;