【问题标题】:Strange behavior of std::string as an argument of a member functionstd::string 作为成员函数的参数的奇怪行为
【发布时间】:2016-06-10 19:06:40
【问题描述】:

我有一堂课:

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

第二个字符串

我发誓我的函数 MyClass::setFilename2 没有错字,而且我没有设置 filename2 变量两次。

我使用的是 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() 返回的指针。你的班级应该有一个 std::string 类型的成员。
  • @CodyGray,谢谢,这是一个很好的观点。但是,如果您发表评论,我怎么能接受您的回答?你能解释一下为什么我的代码现在的行为方式是这样的吗?我预计行 p->setFilename1("first_string"); 之后会发生崩溃,因为我返回的引用不会指向 "first_string",这是一个临时对象。
  • 当你发现自己在写const_cast...你可能做错了什么。

标签: c++ object compilation member undefined-behavior


【解决方案1】:

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;
    }
}

【讨论】:

  • 谢谢!你能想到这个问题的替代解决方案吗?如果我可以将 filename1 和 filename2 存储为 char* 而不是 std::string,这将节省我一些工作。
  • @user2738748 通过手动为您的 c 字符串分配内存,从参数字符串中复制字符并使指针指向分配的内存。
  • @user2738748 这听起来不太可能——std::string 是比char* 更好的存储选择。
  • @user2738748:这将如何为您节省一些工作?你打算用filename1filename2 做什么?
  • @user2738748:您的补救措施比原来的“问题”要糟糕得多。可能原始代码中唯一的问题是他们使用char * 而不是const char * 来保存指向字符串文字的指针;通过引入那些临时的std::string,现在您正在引入严重的生命周期错误。在引入此类问题之前,请花时间正确学习语言;关注警告是可以的,但前提是你真正了解该语言,否则你只能找到复杂的方法来关闭编译器而不实际解决任何问题。
【解决方案2】:
MyClass *p;
p->setFilename1("first_string");
p->printFilename1();
p->setFilename2("second_string");
p->printFilename1();

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

【讨论】:

  • 我确实为指针分配了内存,我在示例中跳过了这部分。
  • @bruffalobill,我刚刚做到了。
【解决方案3】:

例如,我认为这也是错误的。

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;

【讨论】:

  • 为什么添加参考文献有帮助?字符串(引用)仍然是一个临时对象,不是吗?
  • { 字符串 str("something"); } -(相同)→{ string* pstr = new string("something"); delete pstr;} 直到作用域末尾的 'delete' 才存在。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2016-03-02
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-06-27
  • 2013-11-09
  • 1970-01-01
相关资源
最近更新 更多