0

Hello here some of the main member function that constructs my own string class.

When compilers return the new object from friend MyString operator+ function it calls the destructor and then it breaks in the delete[] data;

This how looks part of the class:

class MyString
{
public:

    ...

//create a string from a null-terminated array
MyString(const char *cp):data(new char[std::strlen(cp)+1]), data_length(std::strlen(cp))
{
    std::copy(cp, cp + std::strlen(cp), stdext::checked_array_iterator<char*>(data, data_length));
    data[data_length] = '\0';
}       

    //copy constructor
    MyString(const MyString& str) 
        :data(new char[str.data_length]), 
        data_length(str.data_length)
    {
        std::copy(str.data, str.data + str.data_length, 
        stdext::checked_array_iterator<char*>(data, data_length));
        data[data_length] = '\0';
    }

    //assignment operator #1
    MyString& operator=(const char *cp)
    {
        if (cp != data)
        {
            data_length = std::strlen(cp);
            delete[] data;//delete the old data
            data = new char[std::strlen(cp) + 1];//create new data array
            std::copy(cp, cp + std::strlen(cp), stdext::checked_array_iterator<char*>(data, data_length));
            data[data_length] = '\0';
        }

        return *this;
    }


    //assignment operator #2 with self-assignment check
    MyString& operator=(const MyString& str)
    {
        if (&str != this)
        {
            data_length = std::strlen(str.data);
            delete[] data;//delete the old data
            data = new char[str.size() + 1];//create new data array
            std::copy(str.data, str.data + str.size(), stdext::checked_array_iterator<char*>(data, data_length));
            data[data_length] = '\0';
        }

        return *this;
    }


    //destructor
    ~MyString()
    {
        //free the array
        if (data != nullptr)
        {
            delete[] data;
        }           
    };


    MyString& operator+=(MyString& s)
    {
        std::copy(s.data, s.data + data_length, 
            stdext::checked_array_iterator<char*>(data+data_length, data_length));
        data[data_length + s.data_length] = '\0';
        data_length += s.data_length;
        return *this;
    }

    friend MyString operator+(MyString& s1, MyString& s2)
    {
        MyString str = s1;
        str += s2;
        return str;
    }


private:
    size_t data_length;
    char* data;

};


int main()
{

    MyString mystr1 = "hello";  
    MyString s_con = mystr1 + mystr1;

    return 0;
}
axcelenator
  • 1,497
  • 3
  • 18
  • 42
  • Do you have a `MyString(const char *cp)` constructor you are not showing? – NathanOliver Sep 27 '17 at 16:38
  • @Yes. I will add right now – axcelenator Sep 27 '17 at 16:38
  • 3
    With `clang++ -Wall -Wextra`: *warning: field 'data' will be initialized after field 'data_length' [-Wreorder] :data(new char[str.data_length]),* – chris Sep 27 '17 at 16:39
  • @NathanOliver hello, `MyString(const char *cp)` constr. added – axcelenator Sep 27 '17 at 16:41
  • @chris hello, can't get where is the problem? – axcelenator Sep 27 '17 at 16:43
  • @axcelenator, Fields are initialized in declaration order. The compiler's helping out by pointing out that you're using an uninitialized field. – chris Sep 27 '17 at 16:44
  • 2
    @chris He isn't actually using an uninitialized field. It is just telling him it is ignoring the order in the list. – NathanOliver Sep 27 '17 at 16:45
  • @NathanOliver, Woops, my brain switched the order. – chris Sep 27 '17 at 16:48
  • 4
    Your `operator+=` looks suspect. You never expand the storage for the new string to be added to so your overwriting memory you do not own. That could definitely cause this issue. – NathanOliver Sep 27 '17 at 16:54
  • 2
    [Off Topic] `if (data != nullptr){ delete[] data; }` is not needed. `delete` on a `nullptr` is a non op so you can just use `delete[] data;`. – NathanOliver Sep 27 '17 at 16:56
  • @NathanOliver Hello, I work with the debugger and `operator+=` looks to work nicely. What do you mean I have to expand the storage? `data_length` is grow like this `data_length += s.data_length;` and `std::copy` adds the two strings. In addition, all arrays are guaranteed to be null terminated – axcelenator Sep 28 '17 at 12:34
  • 2
    You increased `data_length` but didn't allocate more space in `data`. Another issue is that your copy constructor doesn't allocate room for the null terminator. – interjay Sep 28 '17 at 12:37
  • Please just use `data` instead of `stdext::checked_array_iterator(data, data_length)` . It's equally safe, and more portable and easier to read – M.M Sep 28 '17 at 12:51
  • @M.M Visual Studio compiler says it is not safe – axcelenator Sep 28 '17 at 12:52
  • @interjay hello how can I allocate more space for a string that it's data array exists? Do I need to delete the old data and create a new extra storage? – axcelenator Sep 28 '17 at 12:59
  • @axcelenator disable that warning, it's just trying to lock you into MS specific code – M.M Sep 28 '17 at 13:12
  • agree that `operator+=` is badly broken – M.M Sep 28 '17 at 13:15
  • @axcelenator if the string `cp` changes (say, mutated by another thread), the `checked_array_iterator` will prevent the buffer overflow. This makes the difference between a denial of service and a remote code execution. – Raymond Chen Sep 28 '17 at 13:52

0 Answers0