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

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

我有一个 class:

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 而且我没有设置 filename2 变量两次。

我使用的是 g++ 编译器版本 4.8.4。这是我如何编译 class:

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++ 的了解相矛盾。如果一个函数不引用相同的变量并且彼此无关,那么一个函数如何影响另一个函数?

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

编辑:你能给我解释一下为什么这段代码会这样吗?

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

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

c_str() returns 指向 char 数组的指针,只要 std::string 未被修改,它就保持有效;在你的例子中,你调用 c_str()std::string 对象在你的方法 returns 之后立即被销毁(它是一个从字符串文字动态创建的临时对象),所以你有效存储指向已释放内存的指针。当您执行 printFileName1 时看到新值的事实只是分配器正在回收之前用于其他字符串的内存位置这一事实的副作用;就标准而言,这都是未定义的行为(你可以预料到悲惨的崩溃)。

正确的做法是将 std::string 直接存储在 class 中,它将在 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;
    }
}

这个例子我觉得也是不对的

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;