【问题标题】:Overwriting custom string failing when non-default ctor was explicitly called显式调用非默认 ctor 时覆盖自定义字符串失败
【发布时间】:2014-07-29 14:30:12
【问题描述】:

对于 Accelerated C++ 的练习,我正在实现一个名为 Str 的自定义字符串类。当底层存储容器是自定义向量 (Vec) 时一切正常,但现在我遇到了一个我不明白的奇怪情况问题。

如果我通过显式调用 Str newStr("some words"); 之类的构造函数来创建一个新的 Str 对象,然后尝试使用 cin >> newStr; 覆盖它,程序最终会崩溃,当它到达 @987654327 时会在调试器中给出一个 SIGABRT @析构函数(就是delete[] data;)。

如果我创建一个新的空Str 然后使用cinStr newStr; cin >> newStr; 一样填充它,或者如果我使用cin 覆盖我使用Str newStr = "some words"; 创建的Str,则不会发生这种情况,它仅在我尝试覆盖通过显式调用非默认构造函数生成的Str 时才会失败,即使此类型在被覆盖之前正确显示。

如果我在创建/显示奇怪行为 Str 和使用 cin 更改其值之间不创建任何新的 Strs,那么在这种情况下,另一个奇怪的事情实际上可以正常工作。

Str a = "Here is a";
Str b("And here is b");

cout << a << endl << b << endl;

Str c = "Finally we have c";
cout << c << endl;
cin >> c;
cout << c << endl << endl;

cin >> b;
cout << b << endl;

这表明您可以成功覆盖Str c,但是当您尝试覆盖Str b 时会崩溃。但是,这可以让您覆盖 b 并且程序成功完成:

Str a = "Here is a";
Str b("And here is b");

cout << a << endl << b << endl;

cin >> b;
cout << b << endl;

Str 包含私有成员 char* dataint length, limit 并且用于 ab 的构造函数是:

Str::Str(const char* cp) {
    limit = length = std::strlen(cp);
    data = new char[length];
    for (size_type i = 0; i != length; ++i)
        data[i] = cp[i];
}

我也有&gt;&gt;friended,它的实现是:

istream& operator>>(istream& is, Str& s) {
    delete[] s.data;
    s.length = s.limit = 0;

    char c;
    while (is.get(c) && isspace(c)) ;

    if (is) {
        do s.push_back(c);
        while (is.get(c) && !isspace(c));

        if (is)
            is.unget();
    }
    return is;
}

我知道push_back 在这个类中可能是一个奇怪的功能,但即使使用cin 很长时间Str 需要很多push_backs,它也能正常工作。我已经尝试调整类/运算符的不同部分几个小时,但我很难过 o_O

最后,我不确定为什么在使用 Str newStr = "blah"; 语法的 Str 初始化期间没有调用我的复制 ctor(使用 couts 检查),即使它有效。

编辑,push_back:

void Str::push_back(char c) {
    if (length == limit) {
        limit = std::max(limit*2, 1);
        char* newData = new char[limit];

        for (size_type i = 0; i < length; ++i)
            newData[i] = data[i];

        delete[] data;
        data = newData;
    }
    data[length++] = c;
}

【问题讨论】:

  • 当您使用底层std::vector 时,内置了push_back() 函数。现在您正在使用字符缓冲区,您必须编写自己的push_back()。看起来这很可能是错误所在。请在此处发布源代码。
  • 当使用Vec 时,我还必须写push_back(),因为该练习涉及制作一个自定义类以像std::vector 一样工作,编辑以显示我的Str::push_back(),似乎有效在其他情况下,当它被多次调用时,会覆盖Str someStr = "asdf";之类的东西
  • 没有明显错误。如果可以发布整个源代码,我会将其粘贴在调试器中,看看会发生什么。
  • 好的,谢谢!尝试一个源代码发布站点:sourcepod.com/vzhhel20-31086 一起编辑了 header/source/main 应该可以作为一个整体工作
  • 刚刚发现与@Dietmar_Kühl 相同的东西。他比我快。 :-)

标签: c++ string cin overwrite


【解决方案1】:

构造函数没有为终止空字符分配空间(也没有存储它)。

我有一种感觉,改成:

Str::Str(const char* cp) {
    limit = length = std::strlen(cp);
    data = new char[length+1];
    for (size_t i = 0; i != length; ++i)
       data[i] = cp[i];
    data[length] = '\0';
}

会解决你的问题。

同时,我建议稍微更改push_back,以便它始终添加一个终止空字符。

void Str::push_back(char c) {
    if (length == limit) {
        limit = (limit > 0 ? limit*2 : 1);
        char* newData = new char[limit+1];

        for (size_t i = 0; i < length; ++i)
            newData[i] = data[i];

        delete[] data;
        data = newData;
    }
    data[length++] = c;
    data[length] = '\0';
}

更新

@Dietmar 的答案是一个关键的修复。不确定这是否就是您所需要的。

【讨论】:

  • 即使进行了此更改,它仍然会崩溃,即使我的代码没有其他部分检查它,我是否需要终止 null?
  • 尝试更新push_back,看看是否有什么不同。
  • 两个函数都复制了,由于某种原因还是一样
  • 添加一个空终止符字符串既没有必要也不能解决问题。
  • @Instinct,Dietmar 建议的答案应该可以解决任何剩余的问题。
【解决方案2】:

delete[]ing 输入运算符中的内存时,您需要确保将data 成员设置为0。如果你不这样做,你将在你的push_back()delete[] 一个已经是delete[]d 的非空指针,并且你的程序将有未定义的行为:

istream& operator>>(istream& is, Str& s) {
    delete[] s.data;
    s.data = 0; // needed to keep your class invariants correct
    s.length = s.limit = 0;
    // ...
}

【讨论】:

  • 啊,没听懂!就是这样,这意味着data 指向的data 内存在调用push_back() 之前以其他方式使用,然后push_back() 删除了该内存(这将有一个未知的、不同的用途)?
  • @Instinc:没有。在delete[] s.data 之后,成员s.data 显然包含一个指向已经delete[]d 内存的指针。然后在您的push_back() 中,您将分配新内存并调用delete[] s.data,它会尝试将delete[] 指向一个已经是delete[]ed 的指针,从而导致未定义的行为(在后台它可能损坏了内存管理系统中的某些内容)。
猜你喜欢
  • 2021-10-13
  • 1970-01-01
  • 2017-02-12
  • 1970-01-01
  • 2012-02-04
  • 2015-03-28
  • 2019-05-20
相关资源
最近更新 更多