-7

My program works fine while I don't have a call to delete[] buf in the destructor. However, when I include it the program crashes at the start of the output. Here are my files.

//string.cpp #include "String.h" #include #include extern ofstream csis;

String::String() {
    buf = "\0";
    length = 0;
}

String::String(const char* tar) {
    int temp = strlen(tar);
    length = temp;
    buf = new char[length + 1];
    buf[length] = '\0';
    for (int i = 0; i < length; i++){
        buf[i] = tar[i];
    }
}

String::String(char a) {
    length = 1;
    buf = new char[length+1];
    buf[length] = '\0';
    buf[0] = a;
}

String::String(int x) {
    int alloc = x;
    if (x < 0) {
        alloc = 0;
    }
    buf = new char[alloc+1];
    length = x;
    buf[0] = '\0';
}


String::String(const String& a) {

    length = a.length;

    buf = new char[length+1];

    for (int i = 0; i < length; i++) {
        buf[i] = a.buf[i];
    }
}

String::String(char a, int x) {
    buf = new char[x+1];
    for (int i = 0; i < x; i++) {
        buf[i] = a;
    }
    buf[x] = '\0';
    length = strlen(buf);
}

String::~String() {
    delete buf;
}


String& String::operator=(const String& tar) {
    buf = new char[tar.length+1];
    strcpy(buf, tar.buf);
    length = tar.length;
    buf[length] = '\0';
    return *this;
}

String& String::operator=(const char* chr) {
    buf = (char*)chr;
    length = int(strlen(chr));
    return *this;
}

String operator+(const String& a, const String& b) {
    String sum;
    int size = a.length + b.length;
    sum.buf = new char[size+1];
    sum.length = size;
    for (int i = 0; i < a.length; i++) {
        sum.buf[i] = a.buf[i];
    }
    int j = 0;
    for (int i = a.length; i < size; i++) {
        sum.buf[i] = b.buf[j];
        j++;
    }
    sum.buf[size] = '\0';
    return sum;
}

String operator+(const String& tar, const char* c) {
    String sum;
    int size = int(strlen(c)) + tar.length;
    sum.buf = new char[size+1];
    sum.length = size;

    for (int i = 0; i < tar.length; i++) {
        sum.buf[i] = tar.buf[i];
    }

    int j = 0;
    for (int i = tar.length; i < size; i++) {
        sum.buf[i] = c[j];
        j++;
    }
    sum.buf[size] = '\0';
    return sum;
}

String operator+(const char* c, const String& tar) {
    String sum;
    int size = int(strlen(c)) + tar.length;
    sum.buf = new char[size+1];
    sum.length = size;
    for (int i = 0; i < int(strlen(c)); i++) {
        sum.buf[i] = c[i];
    }
    int j = 0;
    for (int i = strlen(c); i < size; i++) {
        sum.buf[i] = tar.buf[j];
        j++;
    }
    sum.buf[size] = '\0';
    return sum;
}

String operator+(const String& tar, char c) {
    String sum;
    int size = 1 + tar.length;
    sum.buf = new char[size];
    sum.length = size;
    for (int i = 0; i < tar.length; i++) {
        sum.buf[i] = tar.buf[i];
    }
    int j = 0;
    for (int i = tar.length; i < size; i++) {
        sum.buf[i] = c;
        j++;
    }
    sum.buf[size] = '\0';
    return sum;
}

String operator+(char c, const String& tar) {
    String sum;
    int size = 1 + tar.length;
    sum.buf = new char[size+1];
    sum.length = size;
    for (int i = 0; i < 1; i++) {
        sum.buf[i] = c;
    }
    int j = 0;
    for (int i = 1; i < size; i++) {
        sum.buf[i] = tar.buf[j];
        j++;
    }
    sum.buf[size] = '\0';
    return sum;
}

String& String::operator+=(const String& tar) {
    String temp = *this;
    temp = temp + tar;
    *this = temp;
    return *this;
}

String& String::operator+=(const char c) {
    String temp = *this;
    temp = temp + c;
    *this = temp;
    return *this;
}

String String::operator+() const {
    String sum;
    sum.length = length;
    sum.buf = new char[sum.length+1];
    strcpy(sum.buf, buf);
    for (int i = 0; i < length; i++) {
        sum.buf[i] = toupper(buf[i]);
    }
    sum.buf[length] = '\0';
    return sum;
}

int operator==(const String & tar, const String& tar2) {
    int check = 0;
    if (strcmp(tar.buf, tar2.buf) == 0) {
        check += 1;
    }
    else if (strcmp(tar.buf, tar2.buf) != 0) {
        check = 0;
    }
    return check;
}

int operator!=(const String& tar, const String& tar2) {
    int check = 0;
    if (!(strcmp(tar.buf, tar2.buf) == 0)) {
        check += 1;
    }
    else if (!(strcmp(tar.buf, tar2.buf) != 0)) {
        check = 0;
    }
    return check;
}

int operator<(const String& a, const String& b) {
    int check = 0;
    if (a.length < b.length) {
        check += 1;
    }
    return check;
}

int operator<=(const String& a, const String& b) {
    int check = 0;
    if (a.length <= b.length) {
        check += 1;
    }
    return check;
}

int operator>(const String& a, const String& b) {
    int check = 0;
    if (a.length > b.length) {
        check += 1;
    }
    return check;
}

int operator>=(const String& a, const String& b) {
    int check = 0;
    if (a.length >= b.length) {
        check += 1;
    }
    return check;
}

char& String::operator[](int x) {
    int out;
    if (x >= 0 && x < length) {
        out = x;
    }
    else if (!(x >= 0 && x < length)) {
        int output = NULL;
        cout << "ERROR: Invalid Index with [] operator." << endl;
        csis << "ERROR: Invalid Index with [] operator." << endl;
        out = NULL;
    }
    return buf[out];
}

char* operator+(const String& a, int x) {
    return &a.buf[x];
}

char* operator+(int x, const String& a) {
    return &a.buf[x];
}

String String::operator++(int val) {
    String temp;
    temp = *this;
    for (int i = 0; i < temp.length; i++) {
        temp.buf[i] = temp.buf[i] + 1;
    }
    return temp;
}

String String::operator--(int val) {
    String temp;
    temp = *this;
    for (int i = 0; i < temp.length; i++) {
        temp.buf[i] = temp.buf[i] - 1;
    }
    return temp;
}

String String::operator++() {
    String temp = *this;
    for (int i = 0; i < temp.length; i++) {
        temp.buf[i] = (temp.buf[i] + 1);
    }
    return temp;
}

String String::operator--() {
    String temp = *this;
    for (int i = 0; i < temp.length; i++) {
        temp.buf[i] = (temp.buf[i] - 1);
    }
    return temp;
}

int String::getLength() {
    return length;
}


String String::substr(int a, int b) {
    String temp = *this;
    char *fill = new char[b+1];
    int i = a;
    int x = 0;
    while (x <= b) {
        fill[i] = temp.buf[i];
        i++;
        x++;
    }
    temp.buf = fill;
    temp.buf[length] = '\0';
    return temp;
}

void String::print() {
    cout << """";
    csis << """";
    for (int i = 0; i < length; i++) {
        cout << buf[i];
        csis << buf[i];
    }
    cout << """";
    csis << """";
    cout << "  Length: " << length << endl;
    csis << "  Length: " << length << endl;
}

ostream& operator<<(ostream& o, const String& tar) {
    for (int i = 0; i < tar.length; i++) {
        o << tar.buf[i];
    }
    return o;
}

Here is string.h

//string.h
#ifndef _STRING_H
#define _STRING_H
#include <iomanip>
#include <stdlib.h>
#include <iostream>
using namespace std;

class String {
protected:
    int length;
    char* buf;

public:
    String();
    String(const char*);
    String(char a);
    String(int x);
    String(const String&);
    String(char a, int x);
    ~String();
    // Operator Overload
    String& operator=(const String& tar);
    String& operator= (const char*);
    friend String operator+(const String& a, const String& b);
    friend String operator+(const String&, const char*);
    friend String operator+(const char* c, const String& tar);
    friend String operator+(const String&, char c);
    friend String operator+(char c, const String& tar);
    String& operator+=(const String& tar);
    String& operator+=(const char c);
    String operator+() const;
    friend int operator==(const String&, const String&);
    friend int operator!=(const String&, const String&);
    friend int operator<(const String&, const String&);
    friend int operator<=(const String&, const String&);
    friend int operator>(const String&, const String&);
    friend int operator>=(const String&, const String&);
    char& operator[](int);
    friend char* operator+(const String&, int);
    friend char* operator+(int, const String&);
    String operator++();
    String operator--();
    String operator++(int);
    String operator--(int);
    int getLength();
    String substr(int a, int b);
    void print();
    friend ostream& operator<<(ostream&, const String&);
};
#endif

Here is StringDriver.cpp

// StringDriver.cpp
// MATTHEW BUTNER
// ID: 011029756
#include <iostream>
#include <fstream>
#include <stdlib.h>
#include "StringDriver.h"
using namespace std;
ofstream csis;

int main() {
    csis.open("csis.txt");
    test1();
    test2();
    test3();
    test4();
    test5();
    test6();
    test7();
    test8();
    test9();
    test10();
    test11();
    test12();
    test13();
    test14();
    test15();
    test16();
    test17();
    test18();
    test19();
    test20();
    csis.close();
}

void test1() {
    cout << "1. Testing S1: String default ctor." << endl << endl;
    csis << "1. Testing S1: String default ctor." << endl << endl;
    String s1;
    s1.print();
    wait();
}

void test2() {
    cout << "2. Testing S2: String one arg (char *) ctor." << endl << endl;
    csis << "2. Testing S2: String one arg (char *) ctor." << endl << endl;
    String s2("ABC");
    s2.print();
    wait();
}

void test3() {
    cout << "3. Testing S3: String one arg (char) ctor." << endl << endl;
    csis << "3. Testing S3: String one arg (char) ctor." << endl << endl;
    String s3('Z');
    s3.print();
    wait();
}

void test4() {
    cout << "4. Testing S4: String one arg (int) ctor." << endl << endl;
    csis << "4. Testing S4: String one arg (int) ctor." << endl << endl;
    String s4(10);
    s4.print();
    wait();
}

void test5() {
    cout << "5. Testing S5, T5: String copy ctor." << endl << endl;
    csis << "5. Testing S5, T5: String copy ctor." << endl << endl;
    String s5("Purple Rain");
    s5.print();
    String t5(s5);
    t5.print();
    wait();
}

void test6() {
    cout << "6. Testing S6: String two arg (char, int) ctor." << endl << endl;
    csis << "6. Testing S6: String two arg (char, int) ctor." << endl << endl;
    String s6('*', 10);
    s6.print();
    wait();
}

void test7() {
    cout << "7. Testing S7, T7, U7: String assignment." << endl << endl;
    csis << "7. Testing S7, T7, U7: String assignment." << endl << endl;
    String s7("Sally Ride"), t7, u7;
    t7 = u7 = s7;
    s7.print();
    t7.print();
    u7.print();
    wait();
}

void test8() {
    cout << "8. Testing S8: String assignment." << endl << endl;
    csis << "8. Testing S8: String assignment." << endl << endl;
    String s8("ABC");
    s8 = s8;
    s8.print();
    wait();
}

void test9() {
    cout << "9. Testing S9: Implicit type conversion." << endl << endl;
    csis << "9. Testing S9: Implicit type conversion." << endl << endl;
    String s9;
    s9 = "ABC";
    s9.print();
    wait();
}

void test10() {
    cout << "10. Testing S10, T10, U10: String concatenation." << endl << endl;
    csis << "10. Testing S10, T10, U10: String concatenation." << endl << endl;
    String s10("DEF");
    String t10('H');
    String u10("ABC" + s10 + "G" + t10 + 'I');
    u10.print();
    String v10('X' + u10);
    v10.print();
    wait();
}

void test11() {
    cout << "11. Testing S11, T11: String concatenation." << endl << endl;
    csis << "11. Testing S11, T11: String concatenation." << endl << endl;
    String s11('A');
    String t11("BC");
    s11 += s11 += t11 += 'D';
    s11.print();
    t11.print();
    wait();
}

void test12() {
    cout << "12. Testing S12, T12: String unary operator." << endl << endl;
    csis << "12. Testing S12, T12: String unary operator." << endl << endl;
    String s12("Unary +");
    String t12(+s12);
    s12.print();
    t12.print();
    s12 = +s12;
    s12.print();
    wait();
}

void test13() {
    cout << "13. Testing S13, T13: String comparison operators." << endl << endl;
    csis << "13. Testing S13, T13: String comparison operators." << endl << endl;
    String s13("ABC"), t13("ABCD");
    s13.print();
    t13.print();
    cout << endl;
    cout << "== " << (s13 == t13 ? "True" : "False") << endl;
    cout << "!= " << (s13 != t13 ? "True" : "False") << endl;
    cout << "<  " << (s13 <  t13 ? "True" : "False") << endl;
    cout << "<= " << (s13 <= t13 ? "True" : "False") << endl;
    cout << ">  " << (s13 >  t13 ? "True" : "False") << endl;
    cout << ">= " << (s13 >= t13 ? "True" : "False") << endl;
    csis << endl;
    csis << "== " << (s13 == t13 ? "True" : "False") << endl;
    csis << "!= " << (s13 != t13 ? "True" : "False") << endl;
    csis << "<  " << (s13 <  t13 ? "True" : "False") << endl;
    csis << "<= " << (s13 <= t13 ? "True" : "False") << endl;
    csis << ">  " << (s13 >  t13 ? "True" : "False") << endl;
    csis << ">= " << (s13 >= t13 ? "True" : "False") << endl;
    wait();
}

void test14() {
    cout << "14. Testing S14: Overloaded subscript operator." << endl << endl;
    csis << "14. Testing S14: Overloaded subscript operator." << endl << endl;
    String s14("C++ is fun.");
    for (int i = -1; i <= s14.getLength(); i++) {
        char& ch = s14[i];
        if (ch != '\0')
            ++ch;
    }
    s14.print();
    wait();
}

void test15() {
    cout << "15. Testing S15: Pointer notation." << endl << endl;
    csis << "15. Testing S15: Pointer notation." << endl << endl;
    String s15("ABCDE");
    for(int i = 0; i < s15.getLength(); i++)
        ++(*(s15+i));
    for (int j = 0; j < s15.getLength(); j++) {
        cout << *(j + s15);
        csis << *(j + s15);
    }
    cout << endl;
    csis << endl;
    wait();
}

void test16() {
    cout << "16. Testing S16, T16, U16, V16, W16, X16, Y16, Z16: Increment and decrement operators." << endl << endl;
    csis << "16. Testing S16, T16, U16, V16, W16, X16, Y16, Z16: Increment and decrement operators." << endl << endl;
    String s16("ABC");
    String t16(++s16);
    s16.print();
    t16.print();

    String u16("ABC");
    String v16(u16++);
    u16.print();
    v16.print();

    String w16("ABC");
    String x16(--w16);
    w16.print();
    x16.print();

    String y16("ABC");
    String z16(y16--);
    y16.print();
    z16.print();
    wait();
}

void test17() {
    cout << "17. Testing S17, T17: Substr function." << endl << endl;
    csis << "17. Testing S17, T17: Substr function." << endl << endl;
    String s17("All You Need Is Love"), t17;
    t17 = s17.substr(4, 8);
    s17.print();
    t17.print();
    wait();
}

void test18() {
    cout << "18. Testing S18, T18: Output function." << endl << endl;
    csis << "18. Testing S18, T18: Output function." << endl << endl;
    String s18("Red-");
    String t18("Green-");
    String u18("Blue");
    cout << s18 << t18 << u18;
    csis << s18 << t18 << u18;
    cout << endl;
    csis << endl;
    wait();
}

void test19() {
    cout << "19. Testing S19, T19, U19: ReverseString class." << endl << endl;
    csis << "19. Testing S19, T19, U19: ReverseString class." << endl << endl;
    ReverseString s19("Computer");
    ReverseString t19;
    t19 = ~s19;
    s19.print();
    t19.print();

    ReverseString u19(~~s19);
    u19.print();
    wait();
}

void test20() {
    cout << "20. Testing S20, T20, U20: CaseString class." << endl << endl;
    csis << "20. Testing S20, T20, U20: CaseString class." << endl << endl;
    CaseString s20("BaLLooN");
    CaseString t20;
    t20 = s20;
    s20.print();
    t20.print();
    CaseString u20(s20);
    u20.print();
    wait();
}

void wait() {
    char buf;
    cout << endl << "Press any key to continue." << endl;
    csis << endl << endl;
    cin.get(buf);
}

The problem is with the destructor in string.cpp, when I have an empty destructor, everything goes fine and well, but when I include the delete buf, it'll crash the program. Also, here are the other .h and .cpp files:

//ReverseString.h
#ifndef _REVERSESTRING_H
#define _REVERSESTRING_H
#include "String.h"
#include <iostream>
class ReverseString : public String {
public:
    ReverseString();
    ReverseString(const ReverseString& tar);
    ReverseString(const char* c);
    ReverseString& operator=(const ReverseString&);
    ReverseString operator~();
};
#endif

next file,

//ReverseString.cpp
#include "ReverseString.h"
extern ostream csis;

ReverseString::ReverseString() : String() {
}

ReverseString::ReverseString(const ReverseString& tar) : String(tar) {
}

ReverseString::ReverseString(const char* c) : String(c) {
}

ReverseString& ReverseString::operator=(const ReverseString& tar) {

    length = tar.length;

    buf = tar.buf;

    buf[length] = '\0';



    return *this;
}

ReverseString ReverseString::operator~() {


    ReverseString reverse;

    reverse.length = length;
    reverse.buf = new char[length];

    int j = length - 1;
    for (int i = 0; i < length; i++) {
        reverse.buf[i] = buf[j];
        j--;

    }
    return reverse;
}

CaseString.h

#ifndef _CASESTRING_H
#define _CASESTRING_H
#include "String.h"
#include <iostream>
class CaseString : public String {
protected:
    char* upper;
    char* lower;
public:
    CaseString();
    CaseString(const CaseString& tar);
    CaseString(const char* c);

    CaseString& operator=(const CaseString& c);

    void print();

    ~CaseString();
};
#endif

CaseString.cpp

#include "CaseString.h"
#include <fstream>
extern ofstream csis;

CaseString::CaseString() : String() {
}

CaseString::CaseString(const CaseString& tar) : String(tar) {
    upper = tar.upper;
    lower = tar.lower;
    buf = tar.buf;
    length = tar.length;
}

CaseString::CaseString(const char* c) : String(c) {
    lower = new char[int(strlen(c))];
    upper = new char[int(strlen(c))];
    int losize = strlen(c);
    char* getLow = new char[losize];
    for (int i = 0; i < losize; i++) {
        getLow[i] = tolower(c[i]);
    }
    char* getHi = new char[losize];
    for (int i = 0; i < losize; i++) {
        getHi[i] = toupper(c[i]);
    }
    lower = getLow;
    upper = getHi;
    lower[losize] = '\0';
    upper[losize] = '\0';
}

CaseString& CaseString::operator=(const CaseString& tar) {
    if (&tar != this) {
        String::operator=(tar);
        buf = tar.buf;
        length = tar.length;
        lower = tar.lower;
        upper = tar.upper;
    }
    return *this;
}

void CaseString::print() {
    cout << "\"" << buf << "\"" << " " << "Length = " << length << "  |" << "Lower = " << lower << "  |" << "Upper = " << upper << endl;
    csis << "\"" << buf << "\"" << "  |" << "Length = " << length << "  |" << "Lower = " << lower << "  |" << "Upper = " << upper << endl;
}

CaseString::~CaseString() {
}
rsjaffe
  • 5,600
  • 7
  • 27
  • 39
NULLPTR
  • 37
  • 4
  • 3
    *My program works fine while I don't have a call to delete[]* -- My car works fine if I remove the brakes. The call to `delete[]` failing indicates your program is not running fine, and that `delete[]` is where things finally break down. – PaulMcKenzie Aug 11 '18 at 04:09
  • 1
    I can't see `delete[] buf` in your code, but I see `delete buf`. You code has lot of memory leaks. Also your code can delete memory twice through two pointers to the same memory. – 273K Aug 11 '18 at 04:10
  • same effect happens with delete buf, vs delete[] buf. Both cause a crash – NULLPTR Aug 11 '18 at 04:12
  • That's a lot of code to read through. Why don't you make it easier to debug by narrowing down the problem? Take away test cases until you have just one that crashes. Then eliminate class methods until you have just what you need for that test case (a more minimal example). – JaMiT Aug 11 '18 at 04:12
  • 2
    The issue is *not* the call to delete. You are corrupting memory or mishandling pointers before the call. Don't focus on `delete` -- that is not the issue. Also, why are you including ``, but still writing your own string class? And also `` is a standard header -- you should not be creating a header file with the same name. – PaulMcKenzie Aug 11 '18 at 04:12
  • Advice -- `operator !=` -- There is no need to repeat the code for `==`. All you need is to do this: `int operator!=(const String& tar, const String& tar2) { return !(tar == tar2); }`. Also, why aren't you returning `bool` for these operators? – PaulMcKenzie Aug 11 '18 at 04:23
  • Assignment requires return int; – NULLPTR Aug 11 '18 at 04:32
  • 3
    *Assignment requires return int* -- Goes to show how bad the teaching of C++ is out there. – PaulMcKenzie Aug 11 '18 at 04:38
  • hey, I woulda returned bool if I had the option to do so – NULLPTR Aug 11 '18 at 04:41
  • 1
    You probably need a [good book](https://stackoverflow.com/q/388242/9254539). Whoever insists that a comparison operator return an int is probably a bad teacher. – eesiraed Aug 11 '18 at 04:48
  • @MatthewButner More advice -- before deriving from a base class, test, test, and do more tests on the base class to make sure it is correct. There are several things wrong with the `String` class, but you missed these mistakes and you are deriving more classes from the faulty `String` class. It then becomes much harder to debug, since the foundation itself has bugs. – PaulMcKenzie Aug 11 '18 at 04:53
  • 3
    Please see [mcve] for help in improving your question -- and that exercise may help you find your answer. – rsjaffe Aug 11 '18 at 06:34
  • 1
    Learn to use [valgrind](http://valgrind.org/). Be aware of the [rule of five](https://en.wikipedia.org/wiki/Rule_of_three_(C%2B%2B_programming)). Read [how to debug small programs](https://ericlippert.com/2014/03/05/how-to-debug-small-programs/) – Basile Starynkevitch Aug 11 '18 at 06:34

1 Answers1

2

There are several issues that I see.

The main problem is that you sometimes assign a pointer to a character string to buf (your default constructor is one such place, and there is at least one other). This is a pointer you do not want to delete. You need to either always allocate memory for buf, or have some way to tell if you own the pointer and it should be deleted.

The other issue is that since you use new [], you need to use delete [] buf in your destructor.

In your copy constructor, you do not copy the nul byte at the end of the coped buffer.

1201ProgramAlarm
  • 32,384
  • 7
  • 42
  • 56