自定义字符串类中的Segfault

时间:2014-09-22 01:42:56

标签: c++ string segmentation-fault getline dynamic-arrays

所以我试图完成这个非常基本的字符串类(MyString)。一切似乎都有效,但当我将其上传到任务网站时,它显示出一个段错误。上传站点使用电围栏,但它没有提供有关故障发生位置的深入见解。它基本上贯穿每个函数并为它返回通过/失败/错误。在getline函数的情况下,它返回了一个错误。

此外,上传网站使用valgrind报告没有错误。

编辑:我差点忘了,当我在驱动程序中调用该函数时,它从文件messages.txt读取,其中包含一行文本:Testing this program... PLEASE WORK

以下是getline函数(因为它存在于实现文件中),它似乎是错误的来源:

// reads line from istream ... line end at newline char of choice) -- '\n' in this case
void MyString::getline(istream &inFile, char delimit)
{
    int index = 0;
    do
    {
        data[index] = inFile.get();
        index ++;
        if (index + 1 > capacity)
        {
            MyString tempStr;
            delete [] tempStr.data;
            tempStr.data = new char [capacity];
            for (int i = 0; i <= index; i++)
            {
                tempStr.data[i] = data[i];
            }
            capacity += 5;
            size = index;
            delete [] data;
            data = new char [capacity];
            for (int i = 0; i <= size; i++)
            {
                data[i] = tempStr.data[i];
            }
            delete [] tempStr.data;
            tempStr.data = NULL;
        }
    }
    while (!inFile.eof() && data[index-1] != delimit);
   if (data[index - 1] == delimit)
    {
        index -= 1;
        if (static_cast<double>(index)/capacity < .25 && capacity > 5)
        {
            capacity -= 5;
            char *temp = new char [capacity];
            for (int i = 0; i < index; i++)
            {
                temp[i] = data[i];
            }
            delete [] data;
            data = temp;
        }
    }
    data[index] = '\0';
    size = index + 1;
}

我觉得这是我忽略的一件非常简单的事情,或者是我接触这个特定功能的方式的根本缺陷。任何帮助表示赞赏。我很喜欢编程(几个星期),我只是想维持下去 - 同时注册了CompSci 1 + 2。

此外,下面是更多的实现文件 - 特别是构造函数(减去副本)和一些重载的运算符。虽然我可以在我的头上编译它并成功连接类对象,但上传站点在测试“连接”时返回了一个失败。对于哪个运营商失败没有任何反馈。我很好奇在我的代码中会导致什么。再次感谢。

#include <iostream>
#include <fstream>
#include "MyString.h"

using namespace std;

//default constructor - works
MyString::MyString()
{
    capacity = 5;
    size = 0;
    data = new char [capacity];
}

// constructor with character string
MyString::MyString(const char *cString) 
{
    int index = 0;
    capacity = 5;
    while ( cString[index] != '\0')
    {
        index++;
    }
    size = index + 1;
    while (size > capacity)
    {
        capacity += 5;
    }
    data = new char[capacity];
    for (int i = 0; i < size; i++)
    {
        data[i] = cString[i];
    }
}

// copy constructor
MyString::MyString(const MyString &aMyString)
{
    capacity = aMyString.capacity;
    size = aMyString.size;
    data = new char [capacity];
    for (int i = 0; i < size; i++)
    {
        data[i] = aMyString.data[i];
    }
}
// overloaded += operator
void MyString::operator+=(const MyString &aMyString)
{
    int tSize1 = size;
    int holder = 0;
    size += aMyString.size - 1;
    while (size > capacity)
    {
        capacity += 5;
    }
    char *tempArr = new char [capacity];
    for (int i = 0; i < (tSize1 - 1); i ++)
    {
        tempArr[i] = data[i];
    }
    for (int i = (tSize1 - 1); i < size; i++)
    {
        tempArr[i] = aMyString.data[holder];
        holder ++;
    }
    delete [] data;
    data = tempArr;
}

// overloaded + operator
MyString MyString::operator+(const MyString &aMyString) const
{
    int holder = 0;
    MyString tempS;
    int tSize1 = size + aMyString.size - 1;
    int tCap1 = capacity + aMyString.capacity;
    if (static_cast<double>(tSize1)/tCap1 < .25 && tCap1 > 5)
    {
        tCap1 -= 5;
    }
    tempS.size = tSize1;
    tempS.capacity = tCap1;
    delete [] tempS.data;
    tempS.data = new char [tempS.capacity];
    for (int i = 0; i < (size - 1); i ++)
    {
        tempS.data[i] = data[i];
    }
    for (int i = (size - 1); i < tSize1; i++)
    {
        tempS.data[i] = aMyString.data[holder];
        holder ++;
    }
    return tempS;
}

2 个答案:

答案 0 :(得分:0)

我不知道这些是否都是你的错误,但我看到两个从代码中脱颖而出:

for (int i = 0; i <= index; i++)
{
    tempStr.data[i] = data[i];
}
[snip]
for (int i = 0; i <= size; i++)
{
     data[i] = tempStr.data[i];
}

for语句都访问了多个字符而不是它们应该访问的字符。如果您想要处理5次,例如在基于零的索引编制中,则检查i < 5而不是i <= 5。如果我没有弄错的话,你在这两个for循环中都犯了这个错误。我相信他们应该是:

for (int i = 0; i < index; i++)

for (int i = 0; i < size; i++)

将内存写入数组边缘可能会导致像segfaults这样的问题。

答案 1 :(得分:0)

立即问题出在operator +=上。它应该返回对象的引用,而不是void

其次,operator +应以operator +=开头。相反,你从头开始编写整个operator +&#34;&#34;,在operator + =

中复制代码

以下是如何实施operator +:

// overloaded + operator
MyString MyString::operator+(const MyString &aMyString)
{
    MyString result = *this;   // copy the object
    result += aMyString;   // call the operator += (where the real work is done).
    return result;   // just return the result 
}

您的运营商+ =,需要定义为:

MyString& MyString::operator+(const MyString &aMyString)
{
   // code to do work
   return *this;
}

第三,您未能实现赋值运算符。您实现了复制构造函数,但没有实现赋值运算符。除非您没有发布,否则如果您想将一个字符串分配给另一个字符串,则必须实现它。

最后,您的operator+=有一个缺陷。它会更改size的值:

size += aMyString.size - 1;

然后尝试分配内存。如果内存分配失败(new抛出异常)该怎么办?你如何&#34;回滚&#34; size的值是否为原始值?你不能,至少不是你的实施。

总结一下,仅仅因为你的实施工作&#34;工作&#34;并不意味着它确实运作正常。我上面指出的事情(没有赋值运算符,运算符+ =没有返回引用,*this)只是两个问题。

像这样的作业的问题在于它可以给你错误的成就感,当你如实地编码你所编写的错误时,你从未意识到错误,但更糟糕的是,可以很容易地创造出来。例如:

int main()
{
  MyString s("abc");
  MyString t("123");
  s = t;
}

如果没有赋值运算符,该代码将因内存泄漏和双删除错误而失败。

真正需要一个中级到高级程序员来创建是,这听起来像一个简单的&#34; String&#34;正确地,至少有一个可以传递所有可以使String类在实际程序中可用的测试。