0

I am writing my own String class and have overloaded some of the operators for convenience. But I ran into a problem while using one of these overloaded operators.

Following is the driver code:

String str1 {"Hello"};
String str2 {"World"};

String str3 = str1 + str2;

The above driver code compiled fine but generated the following error when I executed the binary:

free(): double free detected in tcache 2
Aborted (core dumped)

When I commented the last line of the driver code, the file compiled and executed fine. So apparently the problem lies in the third line where I tried using the overloaded + operator which is meant to facilitate concatenation. But still I am not able to spot exactly where the problem is.

Following is the code for destructor:

String::~String() {
    if(this->str != nullptr)
        delete [] this->str;
}

And following is the code for overloaded + operator:

// + overloaded for concatenation of strings
String String::operator+(String &rhs) {
    char *temp_str;
    temp_str = new char[std::strlen(this->str) + std::strlen(rhs.str) + 1];
    temp_str = std::strcat(str, rhs.str);
    String temp_obj {temp_str};
    delete [] temp_str;
    return temp_obj;
}

Following are the constructors:

// Default constructor
String::String(): String(nullptr) {}

// Parameterised constructor
String::String(const char *s): str {nullptr} {
    if(s == nullptr) {
        str = new char;
        *str = '\0';
    }
    else {
        str = new char[std::strlen(s) + 1];
        std::strcpy(str, s);
    }
}

I have also overloaded the = operator for copying and moving objects. Below is the implementation:

// = overloaded to copy a String object
String& String::operator=(const String &rhs) {
    if(this == &rhs)
        return *this;

    delete [] str;
    str = new char[std::strlen(rhs.str) + 1];
    std::strcpy(str, rhs.str);

    return *this;
}


// = overloaded to move a String object
String& String::operator=(String &&rhs) {
    if(this == &rhs)
        return *this;

    str = rhs.str;
    rhs.str = nullptr;

    return *this;
}
  • 3
    I'm pretty sure this `std::strcat(str, rhs.str);` will invoke UB. It will also create a memory leak, check out what [`std::strcat` actually does](https://en.cppreference.com/w/cpp/string/byte/strcat) – Lukas-T May 29 '21 at 12:17
  • The actual problem is probably caused by not correctly implementing the [rule of 3](https://stackoverflow.com/questions/4172722/what-is-the-rule-of-three). But we can't be 100% sure without a [mre]. – Lukas-T May 29 '21 at 12:18
  • Please show a [mre] – Alan Birtles May 29 '21 at 12:19
  • Please don't give us separate slices of the code. Give us something that we can actually compile and run out of the box to see the same problem. Don't dump all of your code at us though (see the link). – HolyBlackCat May 29 '21 at 12:34
  • I suspect, that this might have something to do with the fact, that you didn't define the copy constructor for your class. – Algirdas Preidžius May 29 '21 at 12:37
  • 1
    I believe @churill nailed it. It should be `strcat(strcpy(temp_str, str), rhs.str);` instead of `temp_str = std::strcat(str, rhs.str);`. Otherwise 1. `str` is probably used out-of-bound, and 2. `temp_str` is set to (the address in) `str` while the just allocated memory is lost (and becomes a leak). At least, the 2. is an imaginable reason for a double delete. – Scheff's Cat May 29 '21 at 12:38
  • @Scheff It worked. Thanks a lot! Can you please briefly explain what was going wrong? – Gulshan Mishra May 29 '21 at 12:50
  • 1
    Exactly, what @churill said -> You have used it wrong. Please, follow the link to the [doc](https://en.cppreference.com/w/cpp/string/byte/strcat), provided by churill. Then read again my brief explain why your implementation goes wrong I already gave above. ;-) `std::strcat()` doesn't allocate memory. It requires that the caller provides enough memory in the 1st argument. (And this is what is returned again - just to allow nesting as I did.) So, `temp_str` must be what's given in the 1st argument (and the assignment to `temp_str` is at best useless, and at worst broken code like in your case.) – Scheff's Cat May 29 '21 at 12:52
  • `delete [] this->str;` does not need to pre-check for `nullptr`, since deleting a null pointer is safe (it's a no-op). – Eljay May 29 '21 at 15:00

0 Answers0