String(const char*) constructor memory leak in string class

烈酒焚心 提交于 2021-02-11 14:00:58

问题


So I was trying to implement String class and I got an error saying segmentation fault. I think there is memory leak in my constructor. Can you please tell me what I am doing wrong? Thank you.

here is the constructor code and I am not permitted to use any standard library functionality.

String(const char* chars){
            int i = 0;
            if(chars){
                while(chars[i]){
                    i++;
                }
            }
            len = i;
            str = new char[len - 1];
            for(int j = 0; j < len; j++){
                str[j] = chars[j];
            }
        };

And also this is my full code:

#include <iostream>

using namespace std;

class String{
    public:
        char* str;
        int len;
        String(){
            str = new char[0];
            len = 0;
        };

        String(const char* chars){
            int i = 0;
            if(chars){
                while(chars[i]){
                    i++;
                }
            }
            len = i;
            str = new char[len - 1];
            for(int j = 0; j < len; j++){
                str[j] = chars[j];
            }
        };

        String(const String& s){
            if(s.isEmpty() == false){
                len = s.len;
                str = new char[len];
                for(int i = 0; i < len; i++){
                    str[i] = s.str[i];
                }
            }
        };
        ~String() noexcept{
            if(len > 0)
            delete[] str;
        };

        bool isEmpty() const noexcept{
            if(len == 0){
                return true;
            }
            else{
                return false;
            }
        }

        unsigned int length() const noexcept{
            return len;
        }

        const char* toChars() const noexcept{
            char* temp = new char[len];
            int c = 0;
            while(temp[c] != '\0'){
                temp[c] = str[c];
                c++;
            }
            return temp;
        }
};

int main()
{
    const char* chars = "Boo is snoring";
    String s;
    String t{chars};

    cout << "t.len : " << t.length() << endl << "toChar() : " << t.toChars() << endl; 

    return 0;
}

回答1:


The problem with your String(const char* chars) constructor is on this statement:

str = new char[len - 1];

You are allocating less memory then you need, so your for loop is going out of bounds of the allocated memory. You need to allocate at least len number of chars, not len - 1 number of chars:

str = new char[len];

If you intend str to be null-terminated, you need to allocate len + 1 number of chars instead, and then insert a null terminator after the loop is finished:

str = new char[len + 1];
for(int j = 0; j < len; j++){
    str[j] = chars[j];
}
str[len] = '\0';

You are making a similar mistake in your toChars() method. You are not null-terminating the output, which is required by the operator<< that you are passing the memory to afterwards:

const char* toChars() const {
    char* temp = new char[len + 1];
    for(int c = 0; c < len; ++c){
        temp[c] = str[c];
    }
    temp[len] = '\0';
    return temp;
}

Note that I removed the noexcept on toChars(). This is because new[] is not noexcept, it can throw a std::bad_alloc exception if it can't allocate memory, which you are not catching. noexcept means the method doesn't throw any exception at all, but that is not the case here.

There are other problems with your code, too:

  • Your destructor leaks memory if len is 0, ie if your Default constructor is called, or your Converting constructor is called with a null/empty string. If you call new[], you need to call delete[], regardless of the len used.

  • Your Copy constructor is not initializing str or len if the String being copied is empty.

  • In main(), you are not delete[]'ing the memory that toChars() returns.

  • optional for this particular situation, but in general, you are missing a Copy Assignment operator. And, since you are clearly using C++11 or later, you should also add a Move constructor and a Move Assignment operator as well. See the Rule of 3/5/0.

Try this instead (with no additional library functions added, per request):

#include <iostream>

using namespace std;

class String {
    public:
        char* str = nullptr;
        unsigned int len = 0;

        String() = default;

        String(const char* chars) {
            if (chars) {
                unsigned int i = 0;
                while (chars[i]) {
                    ++i;
                }
                len = i;
                str = new char[len];
                for(int j = 0; j < len; ++j) {
                    str[j] = chars[j];
                }
            }
        }

        String(const String& s) {
            if (!s.isEmpty()) {
                len = s.len;
                str = new char[len];
                for(int i = 0; i < len; ++i){
                    str[i] = s.str[i];
                }
            }
        }

        ~String() noexcept {
            delete[] str;
        }

        String& operator=(const String &s) {
            if (&s != this) {
                String tmp(s);
                char *tmpstr = tmp.str;
                unsigned int tmplen = tmp.len;
                tmp.str = str;
                tmp.len = len;
                str = tmpstr;
                len = tmplen;
            }
            return *this;
        }

        bool isEmpty() const noexcept {
            return (len == 0);
        }

        unsigned int length() const noexcept {
            return len;
        }

        const char* toChars() const {
            char* temp = new char[len + 1];
            for(unsigned int c = 0; c < len; ++c) {
                temp[c] = str[c];
            }
            temp[len] = '\0';
            return temp;
        }
};

int main()
{
    String t{"Boo is snoring"};

    const char *chars = t.toChars();
    cout << "t.len : " << t.length() << endl << "toChar() : " << chars << endl; 
    delete[] chars;

    return 0;
}

Although, a simpler way to implement toChars() would look like this instead:

#include <iostream>

using namespace std;

class String {
    public:
        char* str = nullptr;
        unsigned int len = 0;

        String() = default;

        String(const char* chars) {
            if (chars) {
                unsigned int i = 0;
                while (chars[i]) {
                    ++i;
                }
                len = i;
                str = new char[len + 1];
                for(int j = 0; j < len; ++j) {
                    str[j] = chars[j];
                }
                str[len] = '\0';
            }
        }

        String(const String& s) {
            if (!s.isEmpty()) {
                len = s.len;
                str = new char[len + 1];
                for(int i = 0; i < len; ++i){
                    str[i] = s.str[i];
                }
                str[len] = '\0';
            }
        }

        ~String() noexcept {
            delete[] str;
        }

        String& operator=(const String &s) {
            if (&s != this) {
                String tmp(s);
                char *tmpstr = tmp.str;
                unsigned int tmplen = tmp.len;
                tmp.str = str;
                tmp.len = len;
                str = tmpstr;
                len = tmplen;
            }
            return *this;
        }

        bool isEmpty() const noexcept {
            return (len == 0);
        }

        unsigned int length() const noexcept {
            return len;
        }

        const char* toChars() const noexcept {
            return str ? str : "";
        }
};

int main()
{
    String t{"Boo is snoring"};

    cout << "t.len : " << t.length() << endl << "toChar() : " << t.toChars() << endl; 

    return 0;
}



回答2:


You get crash cause you allocate len - 1 chars new char[len - 1], but copy len chars for(int j = 0; j < len; j++) and do not copy zero-char in the end.

The first constructor should be

String(const char* chars) {
  len = 0;
  if (chars) {
    while (chars[len])
      len++;
  }
  str = new char[len + 1];
  for (int j = 0; j < len; j++){
    str[j] = chars[j];
  }
  str[len] = 0;
};

I hope you are able to update the second constructor properly.

The destructor should be

~String() noexcept{
  delete[] str;
}

I hope you are able to fix other issues.



来源:https://stackoverflow.com/questions/61067994/stringconst-char-constructor-memory-leak-in-string-class

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!