1

I write my own class of string called MyString. In main function I first declare som string and then want to assign a new string to it.

int main()
{
    MyString mystr1(3,'a');
    mystr1 = "hrl"; 
    return 0;
} 

The class looks like that:

class MyString
{
public:

    //default constructor, create an empty string
    MyString() 
    {

    };

    //create a string containing n copies of c
    MyString(size_t n, char c) :data(new char[n+1]), data_length(n)
    {
        for (size_t i = 0; i < n; i++)
        {
            data[i] = c;
        }
        data[n]='\0';
    };

    //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';
    }



    //index operator for writing access
    char& operator[](size_t i)
    {
        return data[i];
    }

    //index operator for read-only access
    const char& operator[](size_t i) const{ return data[i];}

    size_t size() const { return data_length; }

    //destructor
    ~MyString()
    {
        //free the array
        delete[] data;
        delete[] data_array_c_str;  
    };

    const char* _data() const
    {
        return data;
    }

    const char* c_str() const//available only after MyString was created 
    { 
        MyString *ptr = const_cast<MyString*>(this);
        ptr->data_array_c_str = new char[data_length + 1];
        copy(data_array_c_str, data_length, 0);
        data_array_c_str[data_length] = '\0';
        return data_array_c_str;
    }


private:
    size_t data_length;
    char* data;
    char* data_array_c_str;

};

The problem is that after the "hrl" is assigned to mystr1 it calls the destructor trying to delete the private data pointer and the compiler breaks. Can you help me how to fix it? Thanks

axcelenator
  • 1,497
  • 3
  • 18
  • 42
  • How does the compiler break? Also, for this to work, you have to implement your own assignment operator. – Arnav Borborah Sep 18 '17 at 15:22
  • It says exception thrown inside the destructor – axcelenator Sep 18 '17 at 15:23
  • Post the full error and a stack trace. – underscore_d Sep 18 '17 at 15:23
  • @axcelenator Still, your wording is wrong. The compiler doesn't break. – Arnav Borborah Sep 18 '17 at 15:24
  • There is no exception thrown, you have compile errors. The compiler doesn't break, your code breaks. And what exactly is `stdext`? – Passer By Sep 18 '17 at 15:24
  • @NathanOliver: The rule of three is being followed; it's a slightly different bug. – Daniel H Sep 18 '17 at 15:25
  • `Exception thrown at 0x0F93435B (ucrtbased.dll) in 12-1.exe: 0xC0000005: Access violation reading location 0xCCCCCCBC.` – axcelenator Sep 18 '17 at 15:25
  • My guess here you are referring to your application seg faulting? – Carlos Sep 18 '17 at 15:26
  • @DanielH No it's not. There is no custom copy constructor or copy assignment operator. – NathanOliver Sep 18 '17 at 15:26
  • 1
    If you never call the `c_str` method, you never assign to `data_array_c_str`; when you then try to delete it in the constructor, it has some random value and you're invoking undefined behavior by deleting it. You should ensure it's `nullptr` when not in use, because it's safe to `delete` that. Most `std::string` implementations only have one array and always keep it null-terminated to avoid the problem. – Daniel H Sep 18 '17 at 15:26
  • @NathanOliver Oh, you're right. I missed that because the *visible* error was slightly different. – Daniel H Sep 18 '17 at 15:27
  • You need to follow [The Rule of Three](http://stackoverflow.com/questions/4172722/what-is-the-rule-of-three). – R Sahu Sep 18 '17 at 15:28
  • @DanielH even if deleting the line contains `delete[] data_array_c_str` the compiler breaks on `delete[] data` – axcelenator Sep 18 '17 at 15:29
  • Also `new char[std::strlen(cp)]` has an off by one error. `strlen` doesn't count the null terminator so you need `new char[std::strlen(cp) + 1]` to account for that. – NathanOliver Sep 18 '17 at 15:32
  • @NathanOliver hello, when `"hrl"` is assigned to mystr1 the compiler will apply user defined conversion to convert the value to what is needed. So, after the 3rd constructor is called in this situation it calls the destructor. Why? (offsets were fixed inside the question's code) – axcelenator Sep 19 '17 at 13:52
  • 2
    @axcelenator When you do `mystr1 = "hrl"; ` you create a temporary `MyString` and then you assign that to `mystr1` using the default copy assignment operator. This means `mystr1` and the temporary both share the same memory. When the temporary is destroyed it all deletes the memory that `mystr1` points to and when you destroy that on the next line it explodes. – NathanOliver Sep 19 '17 at 13:55
  • @NathanOliver Is every array array deleted inside the destructor should be ensured as `nullptr` if not used? – axcelenator Sep 19 '17 at 14:04
  • @axcelenator Your destructor doesn't set the pointer to `nullptr` so it does delete it twice. This is all explained in the link this is closed to. – NathanOliver Sep 19 '17 at 14:13

0 Answers0